Program listings for the Neighbour-Sensing model of hyphal growth and associated mathematical models of fungal gravitropism

Program listings for the Neighbour-Sensing model of hyphal growth
Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

Limitation of Liability.
  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

Program and coding Copyright (C) Audrius Meškauskas 2017
Text Copyright (C) David Moore 2017
All other rights and trademarks of third parties acknowledged
--------------------------------------------------------------

Directory Structure

C:/neighbour-sensing_model
C:/neighbour-sensing_model/fungi
C:/neighbour-sensing_model/fungi/src
C:/neighbour-sensing_model/fungi/src/fungimedia_3d
C:/neighbour-sensing_model/fungi/src/fungimedia_3d/io
C:/neighbour-sensing_model/fungi/src/fungimedia_3d/Measure
C:/neighbour-sensing_model/fungi/src/fungimedia_3d/Supercomputing
C:/neighbour-sensing_model/fungi/src/geometry
C:/neighbour-sensing_model/fungi/src/man
C:/neighbour-sensing_model/fungi/src/web

--------------------------------------------------------------
Program listings
--------------------------------------------------------------

Listing of text file in subdirectory C:/neighbour-sensing_model/fungi

README.txt
--------------------------------------------------------------

The main executable class of this project is fungimedia_3d.Main. Just run the main method there. The file README.html you will find in the source tree contains description of the command line switches, mostly for headless use in high performance computing.

The program drops XML snapshots as it runs. Each such snapshot can be reloaded, restarting simulation from there. Snapshots can be opened and saved from File menu.

The application can be easily built with almost any Java-aware IDE after the trivial source code import.

Listing of two files in subdirectory C:/neighbour-sensing_model/fungi

.classpath
--------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
	<classpathentry kind="src" path="src"/>
	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
	<classpathentry kind="output" path="bin"/>
</classpath>

.project
--------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
	<name>f3d</name>
	<comment></comment>
	<projects>
	</projects>
	<buildSpec>
		<buildCommand>
			<name>org.eclipse.jdt.core.javabuilder</name>
			<arguments>
			</arguments>
		</buildCommand>
	</buildSpec>
	<natures>
		<nature>org.eclipse.jdt.core.javanature</nature>
	</natures>
</projectDescription>

--------------------------------------------------------------
This concludes the file listings for subdirectory C:/neighbour-sensing_model/fungi
--------------------------------------------------------------

Text listing of .htm file in subdirectory C:/neighbour-sensing_model/fungi/src

README.htm
--------------------------------------------------------------

Calling FungiMedia_3D.jar from the command line

General

FungiMedia_3D.jar is a cross platform java application, able to run on any computers, supporting java 1.4 or compatible. Depending from the platform and from your purpose, you may need to run this file from a command line. On the most of supercomputers, this is the only way to run the program.

The program itself is called by the following command:

java –jar FungiMedia_3D.jar –f ./taskFile.mycelia.xml –c other keys

The following keys are supported

–f file specifies the task file that is usually prepared running program under Windows of other graphic environment. 

-c Blocks the programs graphic user interface. The key is mandatory in a batch mode where the interaction with user is not possible. It can also be used to run prepared task under Windows, resulting in faster execution.

-save interval  . Specifies the interval, in time units, for saving the current situation in a standard task file. –save 1 is used to create sets that can be later converted into animations. If this is not planned, better to use larger values, as saving takes execution time. The default value is 10.

-until  time  . Specifies the time (in time units, not the absolute time) to terminate the program. Is used when we know how far we need to continue.

-r file Specifies (if required) the robot file, containing changes of adjustable parameters.

-output folder Specifies the folder where results must be written. By default, results are written to the folder from where the program was started.

-p number_of_processors Specifies the expected number of processors on a multiprocessor computers. It must be equal of a little larger than the actual number of processors used. Too large values slow the execution. The program cannot use more processors than it was specified with this key.

--------------------------------------------------------------
This concludes this .htm file listing
--------------------------------------------------------------

Program listing of 21 .java files in subdirectory C:/neighbour-sensing_model/fungi/src/fungimedia_3d (arranged in alphabetical order)
File list follows
CheckPointSaver.java
DensityView.java
FieldFinder.java
FreeableListOfPoints.java
Line.java
Linear_substrate.java
Main.java
MOptions.java
MPoint.java
Mycel.java
Orientator.java
PcRobot.java
PointFieldFinder.java
SectFieldFinder.java
Section.java
sliceView.java
Spora.java
Subsegments.java
Substrate.java
Used.java
View.java
--------------------------------------------------------------

CheckPointSaver.java
--------------------------------------------------------------

package fungimedia_3d;

import java.io.IOException;

import fungimedia_3d.io.MyceliaIO;

/**
 * <p>Saves checkpoints at each 30 min of run.</p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public class CheckPointSaver {

  private static long  minutes = 40;

  private static long lastSaved = 0;

  private static String [] cf = new String[]
   {"checkpoint_a.mycelia.xml",
    "checkpoint_b.mycelia.xml"};

  private static int checkpoint = 0;

  public static void getControl(Mycel mycelia)
  {
    try {
    long passed = System.currentTimeMillis()-lastSaved;

    if (passed>=minutes*60*1000)
    {
      try {
        MyceliaIO.store(mycelia, cf[checkpoint]);
        checkpoint = 1 - checkpoint;
        lastSaved = System.currentTimeMillis();
        System.out.println("Checkpoint saving");
      }
      catch (IOException ex1) {
        /*
        String msg = "";
        if (ex1!=null && ex1.getMessage()!=null)
          msg = ex1.getMessage();
        System.out.println("Unable to store checkpoint "+msg);
        */
      }
    }
    }
    catch (SecurityException sec) {};
  }

}

DensityView.java
--------------------------------------------------------------

package fungimedia_3d;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.List;

/**
 * <p> </p>
 * <p>Sight 2.10 System of Internet robots for silicon cloning</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Ulm university</p>
 * @version 2.1.0
 *
 */

public class DensityView extends View implements fungimedia_3d.io.ImageProvider {
  final View view;
  public DensityView(View _view) {
    view = _view;
    mycelia = view.mycelia;
  }

  public Mycel getMycelia() { return mycelia; }

  Image prev = null;
  public int lTime = -1;

  public void paint(Graphics g) {
    deltaX = view.deltaX;
    deltaY = view.deltaY;
    alpha = view.alpha;
    scale = view.scale;

    paintScaleBar(g);

    g.translate(deltaX, deltaY);
    if (view.transSectionPlane) paintTransSectionPlane(g);
    g.setColor(Color.black);
    paintCrossingSections(g, view.mycelia.base);
  }

  /** Save in the AVS-Express-like format. */
  public void save_AVS()
  {
    mycelia.fillStatistics();
    slicePlane = PLANE_Z;

    scale = 1;
    int z0 = (int) (mycelia.b0.z*0.9);
    int zn = (int) (mycelia.bn.z*1.1);
    int dz = (int) (1/scale); // 1 pixel.

    if (dz<1) dz=1;

    H = dz;

    int w = (int) (mycelia.bn.x-mycelia.b0.x);
    int h = (int) (mycelia.bn.y-mycelia.b0.y);

    deltaX = (int) mycelia.b0.x;
    deltaY = (int) mycelia.b0.y;

    BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
    Graphics g = image.getGraphics();
    System.out.println("Saving for AVS (see AVS.dat)...");
    File avsFile = new File("AVS.dat");
    try {
      OutputStream fos =
       new BufferedOutputStream(
       new FileOutputStream(avsFile));

      int WHITE = 0;
      int px;
      for (int z = z0; z <= zn; z += dz) {
        plane = z;
        System.out.println(plane);
        g.setColor(Color.white);
        g.fillRect(0,0,w,h);
        WHITE = image.getRGB(1,1);

        paint(g);

        for (int x = 0; x <w; x++)
        for (int y = 0; y <h; y++) {
         px = image.getRGB(x,y);
         if (px==WHITE) fos.write(0);
                   else fos.write(1);
        }
      }

      PrintStream p = new PrintStream(new FileOutputStream("AVS.txt"));
      p.println("Dimensions "+w+":"+h+":"+(zn-z0));
      p.close();
      System.out.println("Done. Dimensions "+w+":"+h+":"+(zn-z0)+",\n"+
                         "location "+avsFile.getAbsolutePath()+",\nsize "+(avsFile.length()*1E-6)+" mb");
      fos.close();
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  public int slicePlane = 0;

  public static int PLANE_X = 1;
  public static int PLANE_Y = 2;
  public static int PLANE_Z = 4;

  public void setSlicePlane(int plane)
  {
    slicePlane=plane;
    repaint(100);
  }

  public void toggleSlicePlane(int plane)
  {
     setSlicePlane(plane);
  }

  public void paintCrossingSections(Graphics g, Section base)
  {
    if (slicePlane==0) slicePlane = PLANE_Y;
    if ((slicePlane & PLANE_X)!=0)
     {
       paintSubstratesX(g, plane, plane+H);
       paintCrossingSectionsX(g, base);
     }

    if ((slicePlane & PLANE_Y)!=0)
     {
       paintSubstratesY(g, plane, plane+H);
       paintCrossingSectionsY(g, base);
     };

    if ((slicePlane & PLANE_Z)!=0)
     {
      paintSubstratesZ(g, plane, plane+H);
      paintCrossingSectionsZ(g, base);
     }
  }

  public void paintSubstratesX
   (Graphics g, double x0, double xn)
    {
      // paint substrates:
    List substrates = mycelia.substrates;
    if (substrates!=null)
    {
     for (int i = 0; i < substrates.size(); i++) {
       Substrate s = (Substrate) substrates.get(i);

       if (s.center.x+s.R>x0 &&
           s.center.x-s.R<xn )
       s.paint(this,g);
     }
    }
    }

    public void paintSubstratesY
     (Graphics g, double y0, double yn)
      {
        // paint substrates:
      List substrates = mycelia.substrates;
      if (substrates!=null)
      {
       for (int i = 0; i < substrates.size(); i++) {
         Substrate s = (Substrate) substrates.get(i);

         if (s.center.y+s.R>y0 &&
             s.center.y-s.R<yn )
         s.paint(this,g);
       }
      }
      }

      public void paintSubstratesZ
       (Graphics g, double z0, double zn)
        {
          // paint substrates:
        List substrates = mycelia.substrates;
        if (substrates!=null)
        {
         for (int i = 0; i < substrates.size(); i++) {
           Substrate s = (Substrate) substrates.get(i);

           if (s.center.z+s.R>z0 &&
               s.center.z-s.R<zn )
           s.paint(this,g);
         }
        }
        }

  /** x from x0 to xn, y from y0 to yn, x=px, y=? */
  public static final double py(double x0, double xn, double y0, double yn, double px)
  {
    return ((yn-y0)/(xn-x0))*(px-x0)+y0;
  }

  public int H = 10; // Thickness of thranssection

  int H0 = 0; int H1=20;
  /** Transsection over the Y plane (0..y+H. */
  public void paintCrossingSectionsY(Graphics g, Section sec)
  {

    H0 = plane;
    H1 = plane+H;

    double k1 = sec.from.y;
    double k2 = sec.to.y;

    if ((k1>=H0 && k2<=H1) ||
        (k1<=H1 && k2>=H0) )
    {
       sec.paintThisSection_rdc(g, this);
    }

    if (sec.branches!=null && sec.branches.length>0)
    for (int i = 0; i < sec.branches.length; i++) {
      paintCrossingSectionsY(g, sec.branches[i]);
    }

  }

  public Color getLineColor(Section s)
  {
    return s.getSectionColor();
  }

  public void line(Section sec, Graphics g, MPoint a, MPoint b)
  {
    sec.drawColoredLine(g,this ,a,b, sec.getRootDistance());
  }

  /** Transsection over the X plane (0..y+H. */
  public void paintCrossingSectionsX(Graphics g, Section sec)
  {
    H0 = plane;
    H1 = plane+H;

    double k1 = sec.from.x;
    double k2 = sec.to.x;

    if ((k1>=H0 && k2<=H1) ||
        (k1<=H1 && k2>=H0) )
    {
      sec.paintThisSection_rdc(g, this);
    }

    if (sec.branches!=null && sec.branches.length>0)
    for (int i = 0; i < sec.branches.length; i++) {
      paintCrossingSectionsX(g, sec.branches[i]);
    }

  }

  /** Transsection over the Z plane (0..z+H. */
  public void paintCrossingSectionsZ(Graphics g, Section sec)
  {
    H0 = plane;
    H1 = plane+H;

    try {

    double k1 = sec.from.z;
    double k2 = sec.to.z;

    if ((k1>=H0 && k2<=H1) ||
        (k1<=H1 && k2>=H0) )
    {
      sec.paintThisSection_rdc(g, this);
    }
    } catch (Exception exc) {};

    if (sec.branches!=null && sec.branches.length>0)
    for (int i = 0; i < sec.branches.length; i++) {
      paintCrossingSectionsZ(g, sec.branches[i]);
    }

  }

  public void line(Graphics g, MPoint a, MPoint b)
  {
    if (slicePlane==0) slicePlane = PLANE_Y;
    if ((slicePlane & PLANE_X)!=0) lineX(g, a, b);
    if ((slicePlane & PLANE_Y)!=0) lineY(g, a, b);
    if ((slicePlane & PLANE_Z)!=0) lineZ(g, a, b);
  }

  public void dot(Graphics g, MPoint p)
  {
    if (slicePlane==0) slicePlane = PLANE_Y;
    if ((slicePlane & PLANE_X)!=0)
    {
      if (p.x>H0 && p.x<H1) super.dot(g, p);
    } else
    if ((slicePlane & PLANE_Y)!=0)
    {
      if (p.y>H0 && p.y<H1) super.dot(g, p);
    } else
    if ((slicePlane & PLANE_Z)!=0)
    {
      if (p.z>H0 && p.z<H1)
        dotZZ(g,p);
    }
  }

  public void lineX(Graphics g, MPoint a, MPoint b)
  {
    MPoint from, to;

    if (a.x<b.x)
    {
      from = a;
      to   = b;
    }
    else
    {
      to = a;
      from = b;
    }

  if (to.x<H0 || from.x>H1) return; // the line is completely outside

  if ((to.x-from.x)<1E-10) super.line(g, from, to); else
  {
    MPoint f_from = from; MPoint f_to = to;

    if (from.x<H0) // start below the transsection plane
    {
      double z = py(from.x, to.x, from.z, to.z, H0);
      double y = py(from.x, to.x, from.y, to.y, H0);
      f_from = MPoint.New(H0,y,z);
    };

    if (to.x>H1) // end above the transsection plane
    {
      double z = py(from.x, to.x, from.z, to.z, H1);
      double y = py(from.x, to.x, from.y, to.y, H1);
      f_to = MPoint.New(H1,y,z);
    }
    super.line(g, f_from, f_to);
  }
  }

  public void lineY(Graphics g, MPoint a, MPoint b)
  {
        MPoint from, to;

        if (a.y<b.y)
        {
          from = a;
          to   = b;
        }
        else
        {
          to = a;
          from = b;
        }

      if (to.y<H0 || from.y>H1) return; // the line is completely outside

      if ((to.y-from.y)<1E-10) super.line(g, from, to); else
      {
        MPoint f_from = from; MPoint f_to = to;

        if (from.y<H0) // start below the transsection plane
        {
          double z = py(from.y, to.y, from.z, to.z, H0);
          double x = py(from.y, to.y, from.x, to.x, H0);
          f_from = MPoint.New(x, H0, z);
        };

        if (to.y>H1) // end above the transsection plane
        {
          double z = py(from.y, to.y, from.z, to.z, H1);
          double x = py(from.y, to.y, from.x, to.x, H1);
          f_to = MPoint.New(x, H1, z);
        }
        super.line(g, f_from, f_to);
      }
  }

  public void lineZ(Graphics g, MPoint a, MPoint b)
  {
    MPoint from, to;

    if (a.z<b.z)
    {
      from = a;
      to   = b;
    }
    else
    {
      to = a;
      from = b;
    }

  if (to.z<H0 || from.z>H1) return; // the line is completely outside

  if ((to.z-from.z)<1E-10) lineZZ(g, from, to); else
  {
    MPoint f_from = from; MPoint f_to = to;

    if (from.z<H0) // start below the transsection plane
    {
      double x = py(from.z, to.z, from.x, to.x, H0);
      double y = py(from.z, to.z, from.y, to.y, H0);
      f_from = MPoint.New(x,y,H0);
    };

    if (to.z>H1) // end above the transsection plane
    {
      double x = py(from.z, to.z, from.x, to.x, H1);
      double y = py(from.z, to.z, from.y, to.y, H1);
      f_to = MPoint.New(x,y,H1);
    }
    lineZZ(g, f_from, f_to);
  }
  }

  void lineZZ(Graphics g, MPoint a, MPoint b)
   {
    try {
    g.drawLine(
     (int) (scale*(a.x*sin[alpha]+a.z*cos[alpha])),
     (int) (a.y*scale),
     (int) (scale*(b.x*sin[alpha]+b.z*cos[alpha])),
     (int) (b.y*scale)
     );
     } catch (Exception exc)
        {
          System.err.println("a="+a+" and b="+b);
        };
   }

  public Point to2D(MPoint P)
   {
     if (slicePlane!=PLANE_Z)
     {
      return super.to2D(P);
     } else
     {
       return new Point((int) (scale*(P.x*sin[alpha]+P.z*cos[alpha])),
       (int) (P.y*scale));

     }
   }

  void dotZZ(Graphics g, MPoint a)
  {
    g.fillOval(
    (int) (scale*(a.x*sin[alpha]+a.z*cos[alpha]))-2,
    (int) (a.y*scale)-2,4,4);

  }

  static Color[] csc;

  static {
    csc = new Color[250];
    for (int i = 0; i < 25; i++) {
      int f = 255-(i*10);
      csc[i]=new Color(f,f,f);
    }
  }

  public Color getColor(double density)
  {
    int d = (int)Math.round(density);
    if (d>=csc.length) return Color.black;
    return csc[d];
  }

  public void paintScaleBar(Graphics g) {

    int l = (int)(scale*100);
    g.drawLine(10,10,10+l,10);
    g.drawLine(10,5,10,15);
    g.drawLine(l+10,5,l+10,15);
    g.drawString("100 units",10+l/3,30);

    int d = 10+l+20;

    int s = (int)(scale*H);
    g.drawLine(d,10,d+s,10);
    g.drawLine(d,5,d,15);
    g.drawLine(s+d,5,s+d,15);
    g.drawString("Slice thickness, "+H+" units",d+s/3,30);
  }

}

FieldFinder.java
--------------------------------------------------------------

package fungimedia_3d;

/**
 * <p>Field finder interface</p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public interface FieldFinder {

  double findField(MPoint x);

  void moveGrad(MPoint x, MPoint s);
  void moveGrad_i2(MPoint x, MPoint s);
  void moveGrad_i3(MPoint x, MPoint s);

  /** Find the gradient, responsible to the parallel current (parallel tropism). */
  void moveGrad_c(MPoint x, MPoint s);

  /** Find the gradient, responsible to the parallel current (positve/negative tropism). */
  void moveGrad_d(MPoint x, MPoint s);

  /** Get section id. */
  int getSid();
}

FreeableListOfPoints.java
--------------------------------------------------------------

package fungimedia_3d;

/**
 * <p> </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public class FreeableListOfPoints extends java.util.ArrayList {
  public FreeableListOfPoints() {
  }

  public FreeableListOfPoints(int n) {
    super(n);
  }

  protected void finalize()
  {
    for (int i = 0; i < size(); i++) {
      MPoint p = (MPoint) get(i);
      p.free();
    }
  }

}

Line.java
--------------------------------------------------------------

package fungimedia_3d;

import java.awt.Color;

/** 3D line to disply in a view. */
public class Line {

 public MPoint from;
 public MPoint to;

 public Color color = Color.black;

 /** Returns a line with two end co-ordinates. */
 public Line(MPoint f, MPoint t)
  {
    from = f;
    to   = t;
  }

 /** Extends a line from the point "to". */
 public Line extend(MPoint n)
  {
    Line l = new Line(to, n);
    return l;
  }

 public String toString()
  { return from+"="+to; }

 public Line()
  {
  }

 /** The length of the line. */
 public double length()
  {
    try {
    return from.distanceTo(to);
    }
    catch (NullPointerException nul)
     { return 0; }
  }

 public static final double sqr(double x) { return x*x; };
 public static final double sqr(int x) { return x*x; };

}

Linear_substrate.java
--------------------------------------------------------------

package fungimedia_3d;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;

/**
 * Defines linear substrate along y axis.
 * <p>@author Audrius Meskauskas</p>
 * @version 2.1.0
 */

public class Linear_substrate extends Substrate {

  double y,z;

  public Linear_substrate() {
    this(0,40,20);
  }

  public Linear_substrate(double _y, double _z, int _R) {
    y=_y;
    z=_z;
    R=_R;
  }
  public MPoint getGradient(MPoint p) {
    double r= p.sqr(p.z-z)+p.sqr(p.y-y);
    if (r<R*R) return MPoint.New(0,0,0); // we are inside a substrate, ok

    MPoint g = p.Copy();
    g.y = y; g.z=z;
    g.subtract(p);
    g.multiply(-1/r);
    g.multiply(Attractiveness);
    return g;
  }
  public void paint(View v, Graphics g) {
    g.setColor(Color.magenta);
    MPoint a = MPoint.New(0,y,z-R);
    MPoint b = MPoint.New(0,y,z+R);

    Point A = v.to2D(a);
    Point B = v.to2D(b);

    g.fillRect(-1000, A.y, 2000, B.y-A.y);

    v.line(g,a,b);
  }

}

Main.java
--------------------------------------------------------------

package fungimedia_3d;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Toolkit;
import java.io.File;
import java.io.IOException;

import javax.swing.UIManager;

import fungimedia_3d.Supercomputing.ParallelProcessor;
import fungimedia_3d.io.MyceliaIO;
import web.AppFrame;
import web.Growth3D;

public class Main {

  public static final String HEADER = "Neighbour sensing model v "+getVersion()+", by Audrius Meskauskas";

  public static String getVersion()
  {
    if (web.ParameterPanel.FULL_VERSION)
      return "3.7";
     else
      return "2.7";
  }

  /** If true, run in console mode only. */
  public static boolean headless = false;
  public static Frame frame;
  //Main method
  public static void main(String[] args) {
    // save properties

    // search for the key -c:
    headless = false;
    for (int i = 0; i < args.length; i++) {
      if (args[i].equalsIgnoreCase("-c"))
        headless=true;

      if (args[i].equalsIgnoreCase("-save"))
       {
        try {
          int interval = Integer.parseInt(args[i + 1]);
          Mycel.AutoSave.autoSaveInterval = interval;
        }
        catch (NumberFormatException ex) {
          System.out.println("Unable to understand autosave interval: '"+args[i+1]+"'");
        }
       }

       if (args[i].equalsIgnoreCase("-until"))
        {
         try {
           int interval = Integer.parseInt(args[i + 1]);
           Mycel.AutoSave.until = interval;
         }
         catch (NumberFormatException ex) {
           System.out.println("Unable to understand autoStop limit: '"+args[i+1]+"'");
         }
        }

       if (args[i].equalsIgnoreCase("-output"))
        {
         try {
           File f = new File((args[i + 1]));
           f.mkdirs();
           if (!f.exists()) throw new Exception(" unable to create ");
           Mycel.AutoSave.autoSaveFolder = f.getAbsolutePath();
         }
         catch (Exception ex) {
           System.out.println("The output folder: '"+args[i+1]+"' is not valid.");
           ex.printStackTrace(System.out);
         }
        }

      if (args[i].equalsIgnoreCase("-r"))
          {
            String r = args[i+1];
            File robotFile = new File(r);
            if (!robotFile.exists())
            { System.out.println("Robot file "+robotFile.getAbsolutePath()+" specified and not exists!");
              System.exit(1);
            }

            PcRobot.setRobotFile(robotFile);
            System.out.println("Robot file "+robotFile.getAbsoluteFile()+" is applied.");
          }
    }

    if (!headless)
    {

      try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
      } catch (Exception exc)
         { if (exc!=null) System.out.println(exc.getMessage());
           exc.printStackTrace();
         };

        Growth3D applet = new Growth3D();

        applet.isStandalone = true;

        frame = new AppFrame(applet);

        if (Growth3D.primary==null) Growth3D.primary = frame;
        frame.setSize(720,640);
        frame.setTitle(HEADER);
        applet.init();

        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        int rx = (int) (Math.random()*40);
        int ry = (int) (Math.random()*40);
        frame.setLocation(rx+(d.width - frame.getSize().width) / 2, ry+(d.height - frame.getSize().height) / 2);
        frame.setVisible(true);
        applet.start();
        applet.run_parameters(args);
    } else
        start_headless(args);
  }

  /** Load mycelia and start in the headless mode. */
  public static void start_headless(String [] args)
  {
    { Mycel mycelia = null;
      // headless mode
      System.out.println("-c used, starting in console mode only");
      // read paramters:
      if (args.length>0)
      {
        for (int i = 0; i < args.length; i++) {
          if (args[i].equalsIgnoreCase("-f"))
          {
            try {
              mycelia = new Mycel();
              System.out.println("Loading data from "+args[i+1]);
              MyceliaIO.load(mycelia, args[i + 1]);
              mycelia.pause=false;
              i++;
            }
            catch (IOException ex) {
              ex.printStackTrace();
              System.err.println("Unable to open "+args[i+1]);
              System.err.flush();
              System.exit(1);
            }
          }
          if (args[i].equalsIgnoreCase("-p"))
          {
             try {
               ParallelProcessor.N_processors =
                   Integer.parseInt(args[i + 1]);
               i++;
             }
             catch (NumberFormatException ex1) {
               System.out.println("Number of processors, "+args[i+1]+" is not an integer.");
             }
          }

        }
      }

      System.out.println("Processors: "+ParallelProcessor.N_processors);

      if (mycelia==null)
      {
        System.out.println("Please specify -f filaname.mycelia.xml ");
        System.exit(0);
      }
      System.out.println("Running...");
      while (!mycelia.allDead) mycelia.proceed();
    }

  }

}

MOptions.java
--------------------------------------------------------------

package fungimedia_3d;
import java.io.*;
import java.util.*;

import fungimedia_3d.io.*;

/** Central collection for all options. */
public class MOptions implements Cloneable {
 public static String LF = "\r\n";

/** Default (initial) options set. The section of mycelia
 * may change some parameters later as it is holding the
 * copy of d_Options.
 */
 public static MOptions Options = new MOptions();

 /** The set of options. The zero element is identical to
  * the Options field, others hold copies.
  */
 public static MOptions[] R = new MOptions[3];

 static {
   R[0] = Options;
   for (int i = 0; i < R.length; i++) {
     R[i] = Options.Clone();
   }
 }

 /** The class for the Section object that will be generated when
  * the tip will branch. This class must be derived from the
  * fungimedia_3d.Section. If set to null, the
  * new instance of fungimedia_3d.Section() is created during
  * branching.
  */
 public String SectionClassName
     = null; // default, = "fungimedia_3d.Section".
     // "fungimedia_3d.Section";
    // = "fungimedia_3d.Experimental.Stem_000";

 /** General growth of the hyphae and branching. */
 public static class d_Mycelia extends Chapter implements Cloneable {
   /** Branch event occurs if random()<this */
   public  double randomComponent = 0.4;
   public  boolean densityDependend = true;
   /** Sets the general growth velocity. */
   public  double dGrowth = 1;
   /** Default growth vector. */
   public  double defaultGrowthVector = 1;
   /** Default aging quant. */
   public  double dAge = 1.0;

   /** Old hyphae does not grow. */
   public boolean old_ngrow = false;

   /** Old hyphae does not branch. */
   public boolean old_nbranch = false;

   /** Long hyphae does not grow. */
   public boolean long_ngrow = false;

   /** Maximal section length (stops growing after reaching if old_ngrow=true) */
   public  double maxSectionLength = 50;
   /** Maximal section age after that it stops growing if long_ngrow=true*/
   public  double maxGrowingAge = 300;
   /** Maximal section age after that it stops branching */
   public  double maxBranchingAge = 300;
   /** Squared critical distance, under that the other apex is accepted as 'neighouring'. */
   public  double criticalDistance = 400;
   /** Number of neighouring apexes under that the apex is capable to grow. */
   public  double growingDensity = 15;
   /** Number of neighouring apexes  under that the apex is capable to branch. */
   public  double branchingDensity = 2;
   /** Take density into consideration, it is used for growth. */
   public  boolean cbGrowth = false;
   /** Take density into consideration, it is used for branching. */
   public  boolean cbBranch = true;
   /** Optimal initial branch orientation (random otherwise). */
   public  boolean optimalBranchOrientation = false;
   /** Maximal branch angle. */
   public double max_Branch_angle = 180;
   /** Secondary branches (far from the growing tip). */
   public boolean SecondaryBranching = false;
   /** Secondary branching sensitivity adjustment. */
   public double SecondaryBranchingSensAdj = 1.0;
   /** Maximal number of branches from the branch point. */
   public int maxBranchesFromBranchPoint = 8;
   /** Probability of the new branch to become primary. */
   public double prBecomePrimary = 0;

   public boolean relevant(String s)
    {
      if (s.startsWith("maxGrowingAge")) return old_ngrow;
      if (s.startsWith("maxBranchingAge")) return old_nbranch;
      if (s.startsWith("maxSectionLength")) return long_ngrow;
      if (s.startsWith("SecondaryBranchingSensAdj")) return SecondaryBranching;
      return true;
    };

   /** Makes a deep copy of this options set. */
   public String Document()
   {
     StringBuffer b = new StringBuffer();
     java.lang.reflect.Field sf = null;

     try {
     try {
       sf = getClass().getField("suppose");
     }
     catch (SecurityException ex1) {
     }
     catch (NoSuchFieldException ex1) {
     }
      if (sf!=null)
      {
        Object S = sf.get(this);
        if (S instanceof Boolean)
         if (!( (Boolean) S).booleanValue() )
           {
              b.append("<section name=\""+getChapterName()+"\" supposed=\"false\"/>"+LF);
              return b.toString();
           }
      }

      b.append("<section name=\""+getChapterName()+"\" supposed=\"true\">"+LF);

        java.lang.reflect.Field[] f = getClass().getFields();
        for (int i = 0; i < f.length; i++) {
          Object o = f[i].get(this);
          if (!(o instanceof Double && ((Double) o).doubleValue()==Integer.MAX_VALUE))
           b.append("   <parameter name=\""+f[i].getName()+"\" value =\""+o+"\"/>"+LF);
        }
      b.append("</section>");
        return b.toString();
      }
      catch (IllegalAccessException ex) {
       throw new Error(ex);
      }
      catch (IllegalArgumentException ex) {
       throw new Error(ex);
      }
      catch (SecurityException ex) {
       throw new Error(ex);
      }
   }
 }

 public d_Mycelia Mycelia=new d_Mycelia();
 public d_Speed_l_control Speed_l_control=new d_Speed_l_control();
 public d_Field Field=new d_Field();
 public d_Tropisms Tropisms=new d_Tropisms();
 public d_FSensorTurn FSensorTurn = new d_FSensorTurn();
 public d_FSensorTurn_2 FSensorTurn_2 = new d_FSensorTurn_2();
 public d_FSensorTurn_3 FSensorTurn_3 = new d_FSensorTurn_3();
 public d_Gravitropism Gravitropism = new d_Gravitropism();
 public d_PlagioTropism_tolerance PlagioTropism_tolerance = new d_PlagioTropism_tolerance();
 public d_Autotropism_i2 Autotropism_i2 = new d_Autotropism_i2();
 public d_Autotropism_i3 Autotropism_i3 = new d_Autotropism_i3();
 public d_Autotropism_c  Autotropism_c  = new d_Autotropism_c();
 public d_Autotropism_d  Autotropism_d  = new d_Autotropism_d();

 public d_HTropism HTropism = new d_HTropism();

 public d_Destructor Destructor = new d_Destructor();

 public d_Vizualization Vizualization=new d_Vizualization();
 public d_Supercomputing Supercomputing= new d_Supercomputing();
 public d_Comments Comments = new d_Comments();

 /** Vizalization. */
 public static class d_Vizualization extends Chapter implements Cloneable // is not cloned.
 {
  /** Colour mycelia by the root distance. */
  public  boolean useRootDistanceColoring = false;
  /** Colour tips by the density field. */
  public  boolean show_field_vectors = false;
  /** Show the hyphal tips as dots. */
  public boolean show_tips = true;
  /** The current scale of the view. */
  public double scale = 1.0;

  /** Grey scale for publications. */
  public boolean greyScale = false;

  /** The positions of the scroll bars. */
  public int x = 0;
  public int y = 0;
 }

 /** No instance here, used in Mycel. */
 public static class d_Autosave {
   /** Enable autosaving: */
   public boolean autoSave = true;
   /** Auto-drop file name. */
   public String autoSaveFileName = "_";
   /** Interval, in time units, to auto save the current mycelia. */
   public int autoSaveInterval = 10;
   /** Folder to save the files. */
   public String autoSaveFolder = "";
   /** Terminate the program after this time (in time units) is reached. */
   public int until = -1;

   public boolean relevant(String s)
    {
      return false; // this section is not auto-documented in the text
    };

 }

 /** Hyphal death. */
 public static class d_Destructor extends Chapter implements Cloneable
 {
   /** Destroy if the local density at the tip is above this thr. */
   public boolean suppose = false;

   /** Destroy due high local density near the tip. */
   public boolean hd_Destroy = true;
   /** Destroy due low number of supported tips. */
   public boolean st_Destroy = false;
   /** Destroy if the number of supported tips is less than. */
   public int nst_min = 16;

   /** The section must be at least this to be destroyed. */
   public double min_age = 10;
   public boolean l_age_Min = true;
   /** The section must be at most this to be destroyed. */
   public double max_age = 50;
   public boolean l_age_Max = true;

   /** The section must be at least this to be destroyed. */
   public double min_length = 10;
   public boolean l_length_Min = false;
   /** The section must be at most this to be destroyed. */
   public double max_length = 50;
   public boolean l_length_Max = false;

   /** Keep the reference to the dead branches. */
   public boolean keepDeadReferences = true;

   public double  hd = 0.2;
 }

 public static class d_Supercomputing extends Chapter implements Cloneable
 {
   /** Tips per processor. */
   public int min_tips_per_processor=10;
   public boolean relevant(String s)
    {
      return false; // this section is not auto-documented in the text
    };

 }

 /** Field. */
 public static class d_Field extends Chapter implements Cloneable
 {
   /** Complete evalueation (all points of mycelia). Otherwise,
    * only tips and internodes contribute to the field.
    */
   public  boolean completeEvaluation = false;
   /** Tips contribute to the field. */
   public  boolean tips = true;
   /** Branch points contribute to the field. */
   public  boolean branchPoints = true;
   /** Length of mycelia that generates a field, similar to the tip field. */
   public  double chargeUnitLength = 20;

   public boolean relevant(String s)
    {
      if (s.startsWith("chargeUnitLength")) return completeEvaluation;
      if (s.startsWith("branchPoints") || s.startsWith("tips")) return !completeEvaluation;
      return true;
    };

 }

 /* Tropisms */
 public static class d_Tropisms extends Chapter implements Cloneable  {
   /** Density field hypothesis accepted? (Required for all tropisms) */
   public  boolean fieldHypothesis = false;
   /** Autotropic reaction supposed? */
   public  boolean Autotropism = false;
   /** Inertia factor (valid range 0 to 1). */
   public  double InertiaFactor = 0.1;
   /** The autotropism is negative, so this parameter is normally <0. */
   public  double autotropismImpact = -1;
 }

 /** If the current angle deviates from the set plagiogravitropic
  * angle less than by this, no tropic reaction is performed */
 public static class d_PlagioTropism_tolerance extends Chapter implements Cloneable  {
   public int angle=5;
 }

 /** Higher morphogenesis, Orientation field. */
 public static class d_FSensorTurn extends Chapter implements Cloneable  {
   /** Suppose the orientation field like gravitational. */
   public  boolean suppose = false;
   /** The angle of tropic reaction, in degrees, in the orientation field. */
   public  int angle = 90;
   /** The impact of the orientation field.
    * The value by that the vertical (z) co-ordinate of the tropsism vector
    * is increased or decreased. */
   public  double impact = 0.002;
 }

 /** Higher morphogenesis, Orientation field. */
 public static class d_FSensorTurn_2 extends Chapter implements Cloneable  {
   /** Suppose the orientation field like gravitational. */
   public  boolean suppose = false;
   /** The angle of tropic reaction, in degrees, in the orientation field. */
   public  int angle = 90;
   /** The impact of the orientation field.
    * The value by that the vertical (z) co-ordinate of the tropsism vector
    * is increased or decreased. */
   public  double impact = 0.002;
 }

 /** Higher morphogenesis, Orientation field. */
 public static class d_FSensorTurn_3 extends Chapter implements Cloneable  {
   /** Suppose the orientation field like gravitational. */
   public  boolean suppose = false;
   /** The angle of tropic reaction, in degrees, in the orientation field. */
   public  int angle = 90;
   /** The impact of the orientation field.
    * The value by that the vertical (z) co-ordinate of the tropsism vector
    * is increased or decreased. */
   public  double impact = 0.002;
 }

 public static class d_HTropism extends Chapter implements Cloneable
 {
   /** If true, the horizontal plane attracts the hyphae (angle value is ignored). */
   public boolean suppose = false;
   public double impact = 0.002;
   /** Slice thickness. */
   public double layer = 40;
 }

 /** Higher morphogenesis, Orientation field. */
 public static class d_Gravitropism extends Chapter implements Cloneable  {
   /** Suppose the orientation field like gravitational. */
   public  boolean suppose = false;
   /** The angle of tropic reaction, in degrees, in the orientation field. */
   public  int angle = 45;
   /** The impact of the orientation field.
    * The value by that the vertical (z) co-ordinate of the tropism vector
    * is increased or decreased. */
   public  double impact = 0.002;

   /** A type sensor turn (around the hyphal axis). */
   public boolean turnSensorA = false;
   /** Angle to turn the sensor around the hyphal axis. */
   public int turn_A_angle = 45;

   /** Root distance dependent angle turn .*/
   public boolean rootDistanceDependentAngleTurn = false;

   /** To, angle (starts from 'angle')*/
   public double angle_to = 180;
   /** Distance of complete transform. */
   public double complete_turning_at = 500;
   /** Distance to start transform. */
   public double start_turning_at = 100;

   public boolean relevant(String s)
    {
      if (s.startsWith("angle_to") ||
          s.startsWith("complete_turning_at") ||
          s.startsWith("start_turning_at") ) return rootDistanceDependentAngleTurn;
      if (s.startsWith("turn_A_angle")) return turnSensorA;
      return true;
    };

 }

 /** Secondary autotropic reaction (long distance positive). */
 public static class d_Autotropism_i2 extends Chapter implements Cloneable  {
   /** Suppose the orientation field like gravitational. */
   public  boolean suppose = false;
   /** Impact in comparison to the normal strong short distance autotropism. */
   public  double  impact  = 0.0001;
   /** True if the second level autotropism is directly proportional to the
    * squared root of the distance, false if it is inversely proportional.
    */
   public boolean dir_prop = false;
 }

 /** Tertiary autotropic reaction (very long distance). */
 public static class d_Autotropism_i3 extends Chapter implements Cloneable  {
   /** Suppose the orientation field like gravitational. */
   public  boolean suppose = false;
   /** Impact in comparison to the normal strong short distance autotropism. */
   public  double  impact  = 0.0001;
   /** True if the second level autotropism is directly proportional to the
    * squared root of the distance, false if it is inversely proportional.
    */
   public boolean dir_prop = false;
 }

 /** Current driven parallel tropism. */
 public static class d_Autotropism_c extends Chapter implements Cloneable  {
   /** Suppose the orientation field like gravitational. */
   public  boolean suppose = false;
   /** Impact in comparison to the normal strong short distance autotropism. */
   public  double  impact  = 0.1;
 }

 /** Current driven positive tropism. */
 public static class d_Autotropism_d extends Chapter implements Cloneable  {
   /** Suppose the orientation field like gravitational. */
   public  boolean suppose = false;
   /** Impact in comparison to the normal strong short distance autotropism. */
   public  double  impact  = 0.1;
 }

 /** Growth speed proportional the length of the hyphae. */
 public static class d_Speed_l_control extends Chapter implements Cloneable {
   public boolean suppose = false;
   /** Supporting part of the parent segment. */
   public double parent_segment = 10;
   /** Coefficient of proportionality */
   public double coeff = 0.1;
   /** Maximal growth rate. */
   public double maximum = 5;
 }

 public static class d_Comments extends Chapter implements Cloneable {
   public String Comment = "";
   public String CopyRight =
       "Copyright Audrius Meskauskas and David Moore, 2003. All rights reserved.\n"+
       "Contact address david@davidmoore.org.uk, 48, Alan Road, Stockport SK4 4LE, United Kingdom";

   public boolean relevant(String s)
   { if (s.startsWith("CopyRight")) return false;
     if (s.startsWith("Comment")) return Comment.length()!=0;
     return true;
   };

 }
 /** Makes a deep copy of this options set. */
 public MOptions Clone()
 {
    try {
      MOptions n = (MOptions) clone();
      // clone all chapters:
      java.lang.reflect.Field[] f = getClass().getFields();
      for (int i = 0; i < f.length; i++) {
        Object o = f[i].get(this);

        if (o instanceof Chapter) {
          Chapter chapter = (Chapter) o;
          Chapter clonedValue = chapter.Clone();
          f[i].set(n, clonedValue);
        } else
        {
          // cannot clone even cloneable ...
        }
      }
      return n;
    }
    catch (IllegalAccessException ex) {
     throw new Error(ex);
    }
    catch (IllegalArgumentException ex) {
     throw new Error(ex);
    }
    catch (SecurityException ex) {
     throw new Error(ex);
    }
    catch (CloneNotSupportedException ex) {
     throw new Error(ex);
    }
 }

 /** Makes a deep copy of this options set. */
 public void CopyTo(MOptions n)
 {
    try {
      // clone all chapters:
      java.lang.reflect.Field[] f = getClass().getFields();
      for (int i = 0; i < f.length; i++) {
        Object o = f[i].get(this);

        if (o instanceof Chapter) {
          Chapter chapter = (Chapter) o;
          Chapter clonedValue = chapter.Clone();
          f[i].set(n, clonedValue);
        } else
        {
          // cannot clone even cloneable ...
        }
      }
    }
    catch (IllegalAccessException ex) {
     throw new Error(ex);
    }
    catch (IllegalArgumentException ex) {
     throw new Error(ex);
    }
    catch (SecurityException ex) {
     throw new Error(ex);
    }
 }

 /** Document all options sets in R array. */
 public static String DocumentAll()
 {
   StringBuffer b = new StringBuffer();
   b.append("<parameters>");
   for (int i = 0; i < R.length; i++) {
     b.append("<parameter_subset number=\""+i+"\" >");
     b.append(R[i].Document());
     b.append("</parameter_subset>");
   }
   b.append("</parameters>");
   return b.toString();
 }

 /** Document all options sets in R array. */
 public static String DocumentAllText()
 {
   StringBuffer b = new StringBuffer();
   TreeSet def = new TreeSet();
   b.append("Parameters: "+LF);
   for (int i = 0; i < R.length; i++) {
     b.append(LF+"Set "+i+":"+LF);
     b.append(R[i].DocumentText(def));
     b.append(LF);
   }
   return b.toString();
 }

 /** Document this options set. */
 private String DocumentText(Set def)
 {  StringBuffer b = new StringBuffer();

    try {
      MOptions n = (MOptions) clone();
      // clone all chapters:
      java.lang.reflect.Field[] f = getClass().getFields();
      for (int i = 0; i < f.length; i++) {
        Object o = f[i].get(this);

        if (o instanceof Chapter) {
          Chapter chapter = (Chapter) o;
          b.append(chapter.DocumentText(def));
        } else
        {
          // cannot clone even cloneable ...
        }
      }
      return b.toString();
    }
    catch (Exception ex) {
     throw new Error(ex);
    }
 }

 /** Document this options set. */
 private String Document()
 {  StringBuffer b = new StringBuffer();

    try {
      MOptions n = (MOptions) clone();
      // clone all chapters:
      java.lang.reflect.Field[] f = getClass().getFields();
      for (int i = 0; i < f.length; i++) {
        Object o = f[i].get(this);

        if (o instanceof Chapter) {
          Chapter chapter = (Chapter) o;
          b.append(chapter.Document());
        } else
        {
          // cannot clone even cloneable ...
        }
      }
      return b.toString();
    }
    catch (Exception ex) {
     throw new Error(ex);
    }
 }

 public static class Chapter implements Cloneable
  {
    public Chapter Clone()
    {
      try {
        return (Chapter) clone();
      }
      catch (CloneNotSupportedException ex) {
        throw new Error(ex);
      }
    }

    /** Get the name of this chapter. */
    public String getChapterName()
    {
      String s=getClass().getName();
      int p = s.indexOf("$");
      if (p>0) s=s.substring(p+1);
      return s;
    }

    /** Makes a deep copy of this options set. */
    public String Document()
    {
      StringBuffer b = new StringBuffer();
      java.lang.reflect.Field sf = null;

      try {
      try {
        sf = getClass().getField("suppose");
      }
      catch (SecurityException ex1) {
      }
      catch (NoSuchFieldException ex1) {
      }
       if (sf!=null)
       {
         Object S = sf.get(this);
         if (S instanceof Boolean)
          if (!( (Boolean) S).booleanValue() )
            {
               b.append("<section name=\""+getChapterName()+"\" supposed=\"false\"/>"+LF);
               return b.toString();
            }
       }

       b.append("<section name=\""+getChapterName()+"\" supposed=\"true\">"+LF);

         java.lang.reflect.Field[] f = getClass().getFields();
         for (int i = 0; i < f.length; i++) {
           Object o = f[i].get(this);
           b.append("   <parameter name=\""+f[i].getName()+"\" value =\""+o+"\"/>"+LF);
         }
       b.append("</section>");
         return b.toString();
       }
       catch (IllegalAccessException ex) {
        throw new Error(ex);
       }
       catch (IllegalArgumentException ex) {
        throw new Error(ex);
       }
       catch (SecurityException ex) {
        throw new Error(ex);
       }
    }

    /** Makes a deep copy of this options set. */
    public String DocumentText(Set def)
    {
      StringBuffer b = new StringBuffer();
      java.lang.reflect.Field sf = null;

      try {
      try {
        sf = getClass().getField("suppose");
      }
      catch (SecurityException ex1) {
      }
      catch (NoSuchFieldException ex1) {
      }
       if (sf!=null)
       {
         Object S = sf.get(this);
         if (S instanceof Boolean)
          if (!( (Boolean) S).booleanValue() )
            {
               return "";
               //b.append(getChapterName()+" none."+LF);
               //return b.toString();
            }
       }

       b.append(getChapterName().substring(2)+": "+LF); // remove heading "d_" chars

         java.lang.reflect.Field[] f = getClass().getFields();
         String ass;
         boolean paraDoc = false;

         for (int i = 0; i < f.length; i++) {
           Object o = f[i].get(this);
           ass = f[i].getName()+"="+o;

           if (!def.contains(ass) && relevant(ass))
            { b.append(" "+ass);
              def.add(ass);
              paraDoc = true;
              if (i<f.length-1) b.append(", "+LF);
            }
          }
         if (!paraDoc)
          {
            b.setLength(0);
            b.append(getChapterName().substring(2)+" same");
          }
         //if (!paraDoc) return "";
         b.append("."+LF);
         return b.toString();
       }
       catch (Exception ex) {
        throw new Error(ex);
       }
    }

    public boolean relevant(String ass) { return true; };

  }

  /** Set all values in the R array from the given input stream. */
  public static void setAllValuesFrom(InputStream in)
  {
    OptionsReader.setAllValuesFrom(R, in);
    Options = R[0];
  }
}

MPoint.java
--------------------------------------------------------------

package fungimedia_3d;

import java.text.NumberFormat;
import java.util.StringTokenizer;

import man.Recycler;

/** A simple 3D point. */
public class MPoint implements Cloneable {
 /** Co-ordinate X */
 public double x;
 /** Co-ordinate Y */
 public double y;
 /** Co-ordinate Z */
 public double z;

 static double New = 0; static double Rec = 0;

 static class pointControlThread extends Thread
 {
   public void run()
   {
     while (true) {
     try {
       Thread.sleep(25000);
       System.err.println("New "+New+" Recycled "+Rec+" rat "+Rec/New);
       New = Rec = 0;
     }
     catch (InterruptedException ex) {
     }
     }
   }
 }

 //static { new pointControlThread().start(); };

 /** Create point with the given co-ordinates. */
 public static MPoint New(double x, double y, double z)
 {
   MPoint p = _newInstance();
   p.x=x; p.y=y; p.z=z;
   return p;
 }

 /** Create point 0,0,0 */
 public static MPoint New()
 {
   MPoint p = _newInstance();
   p.x=p.y=p.z=0;
   return p;
 }

 /** Create from XML description string. */
 public static MPoint New(String xml)
 {
   MPoint p = _newInstance();
   p.accept(xml);
   return p;
 }

 static final Recycler recycled = new Recycler();

 /** Create point with unpredictable (non-initialized) co-ordinates. */
 public static MPoint _newInstance()
 {
   MPoint p = (MPoint) recycled.get();
   if (p==null)
     {
        New = New+1.0;
        return new MPoint();
     }
      else
     {
        Rec = Rec+1.0;
        return p;
     }
 }

 private MPoint() {};

/** Returns a copy of this point. */
 public final MPoint Copy()
  {
    return new MPoint(x,y,z);
  };

  /** Creates a point with the given co-ordinates. */
  private MPoint (double _x, double _y, double _z)
   {
    x =  _x;
    y =  _y;
    z =  _z;
   }

  /** Returns |this-to| */
  public final double distanceTo(MPoint to)
   {
    double s =
     sqr(x-to.x)+
     sqr(y-to.y)+
     sqr(z-to.z);
    return Math.sqrt(s);
   };

  /** Assign all fields using string description. Expect three
   * space-separated double values. */
  public void accept(String xml)
   {
    try {
      StringTokenizer st = new StringTokenizer(xml);
      x = parseDouble(st.nextToken());
      y = parseDouble(st.nextToken());
      z = parseDouble(st.nextToken());
    }
    catch (NumberFormatException ex) {
      System.err.println(xml);
      ex.printStackTrace();
    }
   }

  /** Idiot-proof parsing. */
  double parseDouble(String x)
  {
    try {
      return Double.parseDouble(x);
    }
    catch (NumberFormatException ex) {
     int a = x.indexOf('.');
     int b = x.lastIndexOf('.');
     if (a!=b) // multiple points fix
       x = x.substring(0,a)+x.substring(a+1);
    return Double.parseDouble(x);
    }
  }

  /** Returns |this| */
  public final double lengthSQ()
  {
    return x*x+y*y+z*z;
  }

  /** Returns |this-to|^2. */
  public final double distanceToSQ(MPoint to)
   {
     return
     sqr(x-to.x)+
     sqr(y-to.y)+
     sqr(z-to.z);
   };

  public String toString()
   { return "("+x+","+y+","+z+")"; };

   public String toString(int decimals)
    {
      NumberFormat f = NumberFormat.getInstance();
      f.setMaximumFractionDigits(decimals);
      f.setGroupingUsed(false);

      return (f.format(x)+" "+f.format(y)+" "+f.format(z)).replace(',','.');
    };

  public static final double sqr(double x) { return x*x; };

  /** Returns this/|this| */
  public final boolean normalize()
  {
    double R = x*x+y*y+z*z;
    if (R<=0)
    {
      //System.out.println("Null gradient vector!");
      return false;
    }
    double k = Math.sqrt(1/R);
    x*=k; y*=k; z*=k;
    return true;
  }

  /**  Returns k*this; */
  public final void multiply(double k)
  {
    x*=k; y*=k; z*=k;
  }

  /** Returns this+p */
  public final void move(MPoint p)
  {
    x+=p.x; y+=p.y; z+=p.z;
  }

  /** Returns this-p */
  public final void subtract(MPoint p)
  {
    x-=p.x; y-=p.y; z-=p.z;
  }

  /** Assign all co-ordinates of the point p to this point. */
  public final void accept(MPoint p)
  {
    x = p.x; y=p.y; z=p.z;
  }

  /** Assign all co-ordinates of the point p to this point. */
  public final void accept(double _x, double _y, double _z)
  {
    x = _x; y=_y; z=_z;
  }

  /** Rotate around the given section, starting by A and ending by B, by angle alpha (clockwise).
   *  A new point is returned, the current point is not modified.
   *  For null or zero length vector, the unrotated copy is returned.
   */
  public final MPoint rotateAround(MPoint A, MPoint B, double angle)
  {
    try {
      MPoint v = new MPoint();
      v.accept(B);
      v.subtract(A);

      MPoint p = new MPoint();
      p.accept(this);
      p.subtract(A);

      p.rotateThisAround(v, angle);
      p.move(A);

      return p;
    }
    catch (NullPointerException ex) {
      return this.Copy();
    }
  }

  /** Rotate around the given vector by angle alpha (clockwise).
   *  A new point is returned, the current point is not modified.
   *  For null or zero length vector, the unrotated copy is returned.
   */

  public final MPoint rotateAround(MPoint vector, double angle)
  {
      try {
     double lx = vector.x; double ly=vector.y; double lz=vector.z;

     double dd = lx*lx+ly*ly+lz*lz;

     if (dd<1E-20) return this.Copy(); // zero length vector

     double d = sqrt(dd);

     double sinad = sin(angle)/d;
     double cosad = (1.0-cos(angle))/dd;

     double lxlx = lx*lx;
     double lzlz = lz*lz;
     double lyly = ly*ly;

     double v1 = sinad*lz;
     double v2 = cosad*ly*lx;
     double v3 = sinad*ly;
     double v4 = cosad*lz*lx;
     double v5 = sinad*lx;
     double v6 = cosad*lz*ly;

     return new MPoint(
      x*(1.0+cosad*(-lzlz-lyly))+y*(-v1+v2)+z*(v3+v4),
      x*(v1+v2)+y*(1.0+cosad*(-lzlz-lxlx))+z*(-v5+v6),
      x*(-v3+v4)+y*(v5+v6)+z*(1.0+cosad*(-lyly-lxlx))
      );
     }
    catch (NullPointerException npe)
    { return this.Copy(); }
  }

  /** Rotate around the given vector by angle alpha (clockwise).
   *  The point is modified.
   *  For null or zero length vector, the unrotated copy is returned.
   */

  public final void rotateThisAround(MPoint vector, double angle)
  {
      try {
     double lx = vector.x; double ly=vector.y; double lz=vector.z;

     double dd = lx*lx+ly*ly+lz*lz;

     if (dd<1E-20) return;

     double d = sqrt(dd);

     double sinad = sin(angle)/d;
     double cosad = (1.0-cos(angle))/dd;

     double lxlx = lx*lx;
     double lzlz = lz*lz;
     double lyly = ly*ly;

     double v1 = sinad*lz;
     double v2 = cosad*ly*lx;
     double v3 = sinad*ly;
     double v4 = cosad*lz*lx;
     double v5 = sinad*lx;
     double v6 = cosad*lz*ly;

     accept(
      x*(1.0+cosad*(-lzlz-lyly))+y*(-v1+v2)+z*(v3+v4),
      x*(v1+v2)+y*(1.0+cosad*(-lzlz-lxlx))+z*(-v5+v6),
      x*(-v3+v4)+y*(v5+v6)+z*(1.0+cosad*(-lyly-lxlx))
      );
     }
    catch (NullPointerException npe)
    { return;}
  }

  /** Dot product. */
  public final double dotProduct(MPoint other) {
     // It is also equal to |A| |B| cos(angle)
     // A * projection of B on A or B * projection of A on B
     return (x * other.x) + (y * other.y) + (z * other.z);
  }

  /** Cross product. */
  public final MPoint crossProduct(MPoint other) {
     double xh = y * other.z - other.y * z;
     double yh = z * other.x - other.z * x;
     double zh = x * other.y - other.x * y;
     return  New(xh, yh, zh);
  }

  public final double length()
  {
    return Math.sqrt(x*x+y*y+z*z);
  }

  /** Get the normalized 0,0,0 - returned value, ortogonal to vector A - this. */
  public final MPoint getOrthogonal(MPoint a)
  {
    double l = 0.001;

    double xh = x-a.x;
    double yh = y-a.y;
    double zh = z-a.z;

    MPoint other;
    if (Math.abs(xh)>l) other = MPoint.New(-xh,yh,zh);
      else
    if (Math.abs(yh)>l) other = MPoint.New(xh,-yh,zh);
      else
    if (Math.abs(zh)>l) other = MPoint.New(xh,yh,-zh);
      else
        other = MPoint.New(Math.random(), Math.random(), Math.random());

    // cross product:
    xh = y * other.z - other.y * z;
    yh = z * other.x - other.z * x;
    zh = x * other.y - other.x * y;

    other.x = xh; other.y=yh; other.z=zh;
    other.normalize();
    return other;
  }

  public boolean equals(Object X)
  {
    if (this==X) return true;
    MPoint p = (MPoint) X;
    //if ((p.x==x) && (p.y==y) && (p.z==z)) return true;
    if (this.distanceToSQ(p)<0.001) return true;
    return false;
  }

  /** The angle between the vectors 0,0,0 - x and 0,0,0 - this, in degrees. */
  public final double angleTo(MPoint x)
  {
    double P = dotProduct(x);
    double A = length();
    double B = x.length();

    double t = P/(A*B);
    return Math.acos(t)*180/Math.PI;
  }

  /** Returns the point as array of float in the form (x,-z,y). Used with javax.3d. */
  public float[] toJ3D(double scale)
  {
    return new float[] {(float)(scale*x), (float)(-scale*z), (float)(scale*y)};
  }

  /** Returns the point as array of float. Used with javax.3d. */
  public float[] toJ3D()
  {
    return new float[] {(float)(x), (float)(-z), (float)(y)};
  }

  final double sin(double x) { return Math.sin(x); }
  final double cos(double x) { return Math.cos(x); }
  final double sqrt(double x) { return Math.sqrt(x); }

  /** Free the point by adding it to recycler. */
  public void free()
  {
    recycled.free(this);
  }

}

Mycel.java
--------------------------------------------------------------

package fungimedia_3d;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import fungimedia_3d.io.MyceliaIO;

/** Represents mycelia of the fungi. */

public class Mycel {
 /** Mycelia of the model. */
 //public static Mycel Mycelia = new fungimedia_3d.testing.whellMycelia();
 public static Mycel Mycelia = new Mycel();

 /** Used Section identifiers. */
 BitSet used_ids = new BitSet();

 /** Action listener commands. */
 public static String STEP = "step";
 public static String RESTART = "restart";

 public List substrates = new ArrayList();

 public Mycel()
 {
   Section.used_ids.clear();
   base = new Spora(this);
   sections[0] = base;
   sections_length = -1;
 }

 public double currentMaximalDistanceFromBase = 0;
 public double currentMaximalDensity = 0;

 /** Bounds of mycelia. */
 public MPoint b0=MPoint.New(), bn=MPoint.New();

 private void mindBounds(MPoint v)
 {
   if (v.x>bn.x) bn.x=v.x;
   if (v.y>bn.y) bn.y=v.y;
   if (v.z>bn.z) bn.z=v.z;

   if (v.x<b0.x) b0.x=v.x;
   if (v.y<b0.y) b0.y=v.y;
   if (v.z<b0.z) b0.z=v.z;
 }

 void fillStatistics()
 {
   currentMaximalDistanceFromBase = sections[0].getRootDistance()+sections[0].length();
   currentMaximalDensity = sections[0].currentDensity;

   b0 = sections[0].to.Copy();
   bn = b0.Copy();
   double srl;

   for (int i = sections_length; i>=0; i--)
   {
     mindBounds(sections[i].to);
     mindBounds(sections[i].from);

     srl = sections[i].getRootDistance()+sections[i].length();

     if (srl>currentMaximalDistanceFromBase)
       currentMaximalDistanceFromBase = srl;
     if (sections[i].currentDensity>currentMaximalDensity)
      currentMaximalDensity=sections[i].currentDensity;
   }
 }

 /** Timer, in agreed time units. */
 public int timer = 0;

 public void restart()
  {
    Section.used_ids.clear();
    base = new Spora(this);
    pause = false;
    timer = 0;
    if (listener!=null)
     listener.actionPerformed(
      new ActionEvent(this,0,RESTART));
  }

 public boolean pause = true;

 /** The root section. */
 public Section base;

 /** Receive the time quant. */
 public void proceed()
  {
   if (pause) return;
   PcRobot.proceed(timer);

   timer++;
   fillPlainNodeArray();
   // Fill-in the number of supported tips
   base.getNumberOfSupportedTips();

   // set used and unused values
   Used.u.update(sections, sections_length);

   precomputeFieldValues_and_proceed();
   checkIfAllDead();
   fillStatistics();
   Autosave();

   ActionEvent proceeded = new ActionEvent(this, timer, STEP);
    try {
      if (listener != null) {
        listener.actionPerformed(proceeded);
      }
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }
  }

 public void paint(Graphics g, View v)
  {
    if (substrates!=null)
    {
     for (int i = 0; i < substrates.size(); i++) {
       Substrate item = (Substrate) substrates.get(i);
       item.paint(v,g);
     }
    }

     fillPlainNodeArray();
     for (int i = sections_length; i>=0; i--)
     {
       sections[i].paintThisSection_rdc(g,v);
     }
  };

 public static class info
  {
    public final int tips;
    public final int bPoints;
    public final double length;
    public final int time;

    public info(int t, int bp, double l, int timer)
     {
      tips = t;
      bPoints = bp;
      length = l;
      time = timer;
     }
    public final String toString()
     {
       double unit = length/tips;
       unit = Math.rint(unit*100)/100;
       double _length = Math.rint(length*100)/100;
       return "time: "+time+" tips: "+tips+" length: "+_length+" unit: "+unit;
     }
  }

 public synchronized info getInfo()
  {
    SC = 0;
    tL = 0;
    BP = 0;
    countSegments(base);
    return new info(SC, BP, tL, timer);
  }

 int SC = 0;
 int BP = 0;
 double tL = 0;
 private void countSegments(Section sec)
  {
    tL+=sec.length();
    if (sec.branches!=null)
     {
       BP++; // this is a branch point
       for (int i = 0; i < sec.branches.length; i++) {
         countSegments(sec.branches[i]);
       }
     } else SC++; // this is a tip
  }

 public double Neighbours(MPoint P, Section s)
 {
   if (s.Options.Tropisms.fieldHypothesis)
   { if (s.pField!=null) return s.pField.field;
     else return 0;
   }
   else
     return Density(P,s);
 }

 public double Density(MPoint p, Section s)
  {
    if (!s.Options.Mycelia.densityDependend) return 0;

    int density = 0;
    double criticalDistance=s.Options.Mycelia.criticalDistance;

    for (int i = sections_length; i>=0; i--) {
     if (sections[i].to.distanceToSQ(p)<criticalDistance)
     density++;
    }
    return density;
  }

   /** The plain array of all sections in mycelia.
    * Automatically updated whily the mycelia proceeds. */
   public Section[] sections=new Section[2000];
   /** The used length of this array minus one.
    *  */
   public int sections_length;

   /** The field finder to compute all fields of mycelia. */
   fungimedia_3d.Supercomputing.CumulativeSectionFieldFinder
    getCumulativeFieldFinder()
   {
     ArrayList finders = new ArrayList(2*sections_length);
     for (int i = sections_length; i>=0; i--)
     {
       sections[i].contributeFinders(finders);
     }
     return new fungimedia_3d.Supercomputing.CumulativeSectionFieldFinder(finders);
   }

   void precomputeFieldValues_and_proceed()
   {
     fungimedia_3d.Supercomputing.ParallelProcessor parallelFinder =
      new fungimedia_3d.Supercomputing.ParallelProcessor();
     parallelFinder.process(sections, sections_length, getCumulativeFieldFinder());
   }

   /** Set to true if all mycelia has died. */
   public boolean allDead = false;

   public void checkIfAllDead()
   {
     int dead = 0;
     for (int i = sections_length; i>=0; i--)
     {
       if (sections[i].isDead()) dead ++;
     }
     if (dead >= sections_length+1)
     {
       System.out.println("Total death of mycelium!!!");
       allDead = true;
     } else allDead = false;

   }

   void fillPlainNodeArray()
   {
      sections_length = -1;
      checkSection(base);

      /*
          for (int i = sections_length; i>=0; i--)
          {
            sections[i]
          }
      */
   }

   void checkSection(Section section)
   {
      try {
        sections_length++;
        sections[sections_length]=section;
      } catch (ArrayIndexOutOfBoundsException nu)
         {
           // extend the array:
           Section [] a = new Section[4*sections.length];
           System.arraycopy(sections, 0, a, 0, sections.length);
           sections = a;
           sections[sections_length]=section;
         };

       if (section.branches!=null)
       {
       int i = section.branches.length;
       while(i>=0)  {
         try {
          checkSection(section.branches[i--]);
         } catch (Exception ex) {}
       }
       }
    }

   /** Write XML information about this mycelia. */
   public synchronized void writeXML(PrintStream s)
   {
     s.println("<moment time = \""+timer+"\">");

     // write the default options:
     s.println("<record>");
     s.println("<date>"+new Date().toString()+"</date>");
     s.println(MOptions.DocumentAll());
     s.println("</record>");

     // write substrates
     s.println("<substrates>");
     Iterator iter = substrates.iterator();
     while (iter.hasNext()) {
       Substrate substrate = (Substrate)iter.next();
       s.println(substrate.toXML());
     }
     s.println("</substrates>");

     s.println("<mycelia age=\""+timer+
               "\" root=\""+base.from.toString(2)+
               "\" nodes=\""+sections_length+"\">");
     fillPlainNodeArray();

     /*
     // now must be more carefull with ids.
     // repetetive ids are avoided with synchronized in Section constructor
     for (int i = 0; i<=sections_length; i++)
     {
       // reassigh ids being sure they are unique:
       Section e = sections[i];
       e.id = i+200;
     }

     for (int i = 0; i<=sections_length; i++)
     {
       // reassigh b_id being sure they are unique:
       Section e = sections[i];
       if (e.base!=null)
          e.b_id = e.base.id;
        else
          e.b_id=0;
     }
     */

     // finally, write:
     for (int i = 0; i<=sections_length; i++)
     {
       s.println(sections[i].toXML());
     }

     s.println("</mycelia>");

     s.println("</moment>");
   }

   /** Read mycelia description, expect xml information. */
   public void readXML(BufferedReader in) throws IOException
   {
     Section.used_ids.clear();

     String s;
     StringBuffer b = new StringBuffer();
     substrates = new ArrayList();

     // skip the heading information
     while ( (s=in.readLine()).indexOf("<mycelia ")<0)
     {
       // substrates must be defined before mycelia.
       if (s.indexOf("<substrate")>=0 && s.indexOf("<substrates")<0)
         substrates.add(new Substrate(s));
     }

     // get age if available:
      try {
        String age = "age";
        int a = s.indexOf(age);
        if (a >= 0) {
          a = s.indexOf('"', a);
          int aa = s.indexOf('"', a + 1);
          timer = Integer.parseInt(s.substring(a + 1, aa));
        }
      }
      catch (NumberFormatException ex) {
        System.out.println("Mycelia age is represented in not undersandable form.");
      }

     ArrayList nodes = new ArrayList();
     // read all sections:

     StreamTokenizer t = new StreamTokenizer(in);
     t.ordinaryChar(' ');
     t.ordinaryChar('/');
     int x;
     char cx;
     Sections:
     while (true)
     {
       x = t.nextToken();
       cx = (char) x;
       if (x==t.TT_EOF) break Sections;
       if (x==t.TT_WORD)
        {
          if (t.sval.equals("mycelia"))
           break Sections;

          if (t.sval.equals("sec")) // read the section info
           {

             b.setLength(0);
             b.append("<sec");
             String d;
             Section:
             while (true)
             {
               x = t.nextToken(); cx = (char) x;
               if (x==t.TT_EOF) break Section; else
               if (x==t.TT_WORD)
               {
                 if (t.sval.equals("sec"))
                  {
                    b.append("sec>");
                    break Section;
                  } else b.append(t.sval);
               } else
               if (x==t.TT_NUMBER)
                 b.append(Double.toString(t.nval));
                 else
               if (x=='\"')
                 b.append('"'+t.sval+'"');
                 else
                  b.append( (char) x);
             }
             nodes.add(new Section(this, b.toString()));
           }
        }
     }

     // make an array:
     sections = new Section[nodes.size()+100];
     sections_length = nodes.size()-1;

     for (int i = 0; i <= sections_length; i++) {
      sections[i]=(Section) nodes.get(i);
     }

     Section e;
     for (int i = sections_length; i>=0; i--)
     {
       e = sections[i];
       e.mycel = this;
       if (e.b_id==0)
         { base = e;
           base.from = MPoint.New();
         }
       // need to set branches
       else
       {
         Section master = findSection(e.b_id);
         e.base = master;
         e.from = master.to;

         if (master.branches==null || master.branches.length==0)
          master.branches = new Section[] { e };
          else
          {
            if (master.branches.length==1)
            {
              master.branches = new Section[]
                  { master.branches[0], e };
            }
            else
            {
             /*
             if (i_findSection(e.id)!=i_findSectionR(e.id))
              System.out.println("ERROR: multiple occurences of id "+e.id);
             */
             // multiple branches
             Section nbranches[] = new Section[master.branches.length+1];
             for (int ii = 0; ii < master.branches.length; ii++) {
               nbranches[ii]=master.branches[ii];
             }
             nbranches[nbranches.length-1] = e;
             master.branches = nbranches;
            }
          }
       }
     }

     // compute parameters for better displaying:
     for (int i = sections_length; i>=0; i--)
     {
       sections[i].RootDistance=sections[i].getRootDistance();
       sections[i].setOptionsSetFromFlags();
     }
     fillStatistics();
     base.getNumberOfSupportedTips(); // fill-in the values of supported tips
   }

   /** Find the section with this id.
    * @todo: optimize using binary search
    */

   Section findSection(int id)
   {
     for (int i = 0; i<=sections_length; i++)
     {
       if (sections[i].id==id) return sections[i];
     }
     throw new Error("Section with "+id+" not found.");
   }

   /** Reverse search (other direction). */
   int i_findSectionR(int id)
   {
     for (int i = sections_length; i>=0; i--)
     {
       if (sections[i].id==id) return i;
     }
     throw new Error("Section with "+id+" not found.");
   }

   int i_findSection(int id)
   {
     for (int i = 0; i<=sections_length; i++)
     {
       if (sections[i].id==id) return i;
     }
     throw new Error("Section with "+id+" not found.");
   }

   public static final MOptions.d_Autosave AutoSave = new MOptions.d_Autosave();

   public void Autosave()
   {
     try {
       // Do autosavings:
       if (AutoSave.autoSave) {
         if ( (timer % AutoSave.autoSaveInterval) == 0) {
           try {
             String dir = AutoSave.autoSaveFolder;
             if (dir.length()>0)
               dir = dir + File.separator;
             System.out.println("Saving " + timer + " " + getInfo().toString());
             MyceliaIO.store(this,
                             dir+
                             AutoSave.autoSaveFileName +
                             "_" +
                             timer + ".mycelia.xml");
           }
           catch (SecurityException sec) {}
           catch (Exception ex) {
             ex.printStackTrace();
           }
         }
       }

       CheckPointSaver.getControl(this);

       if (AutoSave.until>0)
         if (AutoSave.until<=timer)
         {
           System.out.println("The given time limit, "+AutoSave.until+" is reached.");
           System.out.println("Normal exit.");
           System.exit(0);
         }
     }
      catch (SecurityException sec) {
        sec.printStackTrace();
     }

   }
  public void setListener(ActionListener listener) {
    this.listener = listener;
  }
  public int getTimer() {
    return timer;
  }

  ActionListener listener = null;
}

Orientator.java
--------------------------------------------------------------

package fungimedia_3d;

import java.util.ArrayList;
import java.util.ListIterator;
import java.util.List;

/**
 * <p>The Orientator is responsible for producing the tropism orientation vector. </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public class Orientator {

  public final Section section;

  public Orientator(Section s) {
    section  = s;
  }

  public final double getAngle(double r, double z)
  {
    return (180.0/Math.PI)*Math.atan2(r,z);
     // (our z axis increases while going down)
  }

  public final double getGravitropicAngle()
  {
    MOptions.d_Gravitropism g = section.Options.Gravitropism;
    if (g.rootDistanceDependentAngleTurn)
    {
      double r = section.getRootDistance();
      if (r<=g.start_turning_at) return g.angle;
      if (r>g.complete_turning_at) return g.angle_to;

      r = r-g.start_turning_at;
      double rf = g.complete_turning_at-g.start_turning_at;
      if (rf==0) return g.angle_to;

      double sf = g.angle_to-g.angle;

      return sf*(r/rf)+g.angle;

    }
     else
       return g.angle;
  }

  public final double get_g_Vector(double r, double z)
  {
    double beta = getGravitropicAngle();
    double alpha = getAngle(r,-z);
    return ang_signum(beta-alpha);
  }

  /** Get tropism vector. The first member in the array is the
   * computed tropism vector. The subsequent members are the
   * explaining components (how computed). [1] is the density field
   * component and [2] is the orientation field component.
   */

  public MPoint [] getTropismVector()
  {
    MPoint p = section.to;
    MPoint g;
    if (section.Options.Tropisms.Autotropism)
      g=section.pField.gradient.Copy();
      else
      g=MPoint.New();

    g.multiply(section.Options.Tropisms.autotropismImpact);
     // The autotropism is negative, so this parameter is normally <0

    MPoint R = g.Copy();

    ArrayList components = new FreeableListOfPoints();
    components.add(g);

    MPoint or = section.getOrientationVector();

    if (section.Options.FSensorTurn.suppose)
     {
       g = g.rotateAround(or, section.Options.FSensorTurn.angle);
     };

    if (section.Options.Gravitropism.suppose)
    if (section.Options.Gravitropism.impact!=0)
    {
     double r = Math.sqrt(g.x*g.x+g.y*g.y);
     MPoint of = getGravitropicVector(section);
     if (section.Options.Gravitropism.turnSensorA)
      of = of.rotateAround(or, section.Options.Gravitropism.turn_A_angle);
     R.move(of);
     components.add(of);
    };

    if (section.Options.HTropism.suppose)
    {
       double h = section.Options.HTropism.layer*0.5;
       if (section.to.z>h)
              R.z-= section.Options.HTropism.impact;
              else
       if (section.to.z<-h)
              R.z+=section.Options.HTropism.impact;
    };

    if (section.Options.Autotropism_i2.suppose)
    {
    if (section.Options.Autotropism_i2.impact!=0)
    {
       MPoint of = section.pField.gradient_i2.Copy();

       // (positive  by default)
       double impact = section.Options.Autotropism_i2.impact;

       of.multiply(impact);
       R.move(of);
       components.add(of);
    };
    }

    if (section.Options.Autotropism_i3.suppose)
    {
    if (section.Options.Autotropism_i3.impact!=0)
    {
       MPoint of = section.pField.gradient_i3.Copy();

       // (positive  by default)
       double impact = section.Options.Autotropism_i3.impact;

       of.multiply(impact);
       R.move(of);
       components.add(of);
    };
    }

    if (section.Options.Autotropism_c.suppose)
    {
    if (section.Options.Autotropism_c.impact!=0)
    {
       MPoint of = section.pField.gradient_c.Copy();

       // (positive  by default)
       double impact = section.Options.Autotropism_c.impact;

       of.multiply(impact);
       R.move(of);
       components.add(of);
    };
    }

    if (section.Options.Autotropism_d.suppose)
    {
    if (section.Options.Autotropism_d.impact!=0)
    {
       MPoint of = section.pField.gradient_d.Copy();

       // (positive  by default)
       double impact = section.Options.Autotropism_d.impact;

       of.multiply(impact);
       R.move(of);
       components.add(of);
    };
    }

    List substrates = section.getMycel().substrates;
    if (substrates!=null)
    {
      MPoint of = MPoint.New();
      for (int i = 0; i < substrates.size(); i++) {
       Substrate s = (Substrate) substrates.get(i);
       of.move(s.getGradient(p));
      }
      components.add(of);
      R.move(of);
    }

    MPoint[] a = new MPoint[components.size()+1];
    a[0]=R;
    for (int i = 0; i < a.length-1; i++) {
     a[i+1]= (MPoint) components.get(i);
    }

    return a;
  }

  public final double ang_signum(double x)
  {
    x = Math.rint(x);
    double tolerance = section.Options.PlagioTropism_tolerance.angle;
    if (x<tolerance && x>-tolerance) return 0;
    if (x<0) return -1;
    return 1;
  }

  public MPoint getGravitropicVector(Section s)
  {
    MPoint ao = s.to.Copy();

    MPoint from;

    if (s.subsegments==null || s.subsegments.size()==0) from = s.from;
      else
      {
        ListIterator list = s.subsegments.listIterator(s.subsegments.size());
        from  = (MPoint) list.previous();
        while (list.hasPrevious() && ao.distanceToSQ(from)<9)
         from = (MPoint) list.previous();
      }

    ao.subtract(from);
    double r=Math.sqrt(ao.x*ao.x+ao.y*ao.y);
    double gz =get_g_Vector(r,ao.z);
    MPoint gg=MPoint.New(0,0, s.Options.Gravitropism.impact*gz);
    return gg;
  }

 public MPoint checkSection(double x, double y)
 {
   Section s = new Section();
   s.from=MPoint.New(10,10,10);
   s.to=MPoint.New(10+x,10+x,10+y);
   MPoint g =(getGravitropicVector(s));
   return g;
 }

}

PcRobot.java
--------------------------------------------------------------

package fungimedia_3d;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.StringTokenizer;
/**
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public class PcRobot {
  static ArrayList moments = null;
  private static boolean active = false;
  static File robot = null;

  public static void setRobotFile(File r)
  {
    moments = null;
    robot = r;
    if (r!=null && r.exists())
           active = true;
      else active = false;
  }

  public static void proceed(int time)
  {
    if (!active) return;
    time = time;
    if (moments==null)
    try {
      if (robot==null) return;
      if (!robot.exists()) return;
      System.out.println("Loading robot file "+robot.getAbsolutePath());
      if (moments == null) { // read moments
        BufferedReader bu = new BufferedReader(new FileReader(robot));
        String s;

        moments = new ArrayList();

        while ( (s = bu.readLine()) != null) {
          StringTokenizer st = new StringTokenizer(s);
          int t = Integer.parseInt(st.nextToken());
          int g = (int) Double.parseDouble(st.nextToken());
          double a = Double.parseDouble(st.nextToken());
          try {
            double ac = Double.parseDouble(st.nextToken());
            double ad = Double.parseDouble(st.nextToken());
            moments.add(new Moment(t, g, a, ac, ad));
          }
          catch (Exception ex1) {
            moments.add(new Moment(t, g, a, 0, 0));
          }
        }
        bu.close();
      }
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }

    Moment m = null;
    int i;
    // find the moment
    for (i = 0; i < moments.size(); i++) {
     m = (Moment) moments.get(i);
     if (m.time>=time) break;
    }

    if (i>0)
     m = (Moment) moments.get(i-1);
     if (m==null && moments.size()!=0) m = (Moment) moments.get(0);

     if (m!=null)
       m.performChanges();
  }

  static class Moment {
    double time;
    int g;
    double a, ac, ad;
    Moment(double _t, int _g, double _a, double _ac, double _ad)
    {
      g = _g; a=_a;
      ac = _ac; ad = _ad;
      time = _t;
    }

    void performChanges()
    {
      MOptions.Options.Autotropism_i2.impact = a;
      MOptions.Options.Gravitropism.angle = g;
      MOptions.Options.Autotropism_c.impact = ac;
      MOptions.Options.Autotropism_d.impact = ad;
    }
  }

}

PointFieldFinder.java
--------------------------------------------------------------

package fungimedia_3d;

/**
 * <p>Computes a field of a single point.
 * THIS CLASS USES POINT REFERENCE TO SECTION. </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public class PointFieldFinder implements FieldFinder {
  private final MPoint p;
  private final int sid;
  private final boolean i2_dir;

  public int getSid() { return sid; };

  public PointFieldFinder(MPoint P, Section s) {
    p = P; //new finalPoint(P);
    sid = s.id;
    i2_dir = s.Options.Autotropism_i2.dir_prop;
  }

  public double findField(MPoint x)
  {
    double l = x.distanceToSQ(p);
    if (l>1E-20) return 1/l;
    return 0;
  }

  public MPoint findGrad(MPoint x)
  {
    double dx, dy, dz;
    double dxdx, dydy, dzdz;
    double V;

    dx = x.x-p.x;
    dy = x.y-p.y;
    dz = x.z-p.z;

    dxdx = dx*dx;
    dydy = dy*dy;
    dzdz = dz*dz;

    V = dxdx+dydy+dzdz;

    if (V>0)
    {
      V=V*V;
      return MPoint.New(-2*dx/V,-2*dy/V,-2*dz/V);
    } else return MPoint.New();
  }

  public MPoint findGrad_i2(MPoint x)
  {
    double dx, dy, dz;
    double dxdx, dydy, dzdz;
    double V;

    dx = p.x-x.x;
    dy = p.y-x.y;
    dz = p.z-x.z;

    dxdx = dx*dx;
    dydy = dy*dy;
    dzdz = dz*dz;

    V = dxdx+dydy+dzdz;

    if (i2_dir)
     V = Math.sqrt(V*V*V);
     else
     V = Math.sqrt(V*V*V*V*V);
     V = (Math.sqrt(V)); // make even more far distance

    if (V>1E-30)
    {
      V=V*V*2; // incorporationg 1/2
      return MPoint.New(dx/V,dy/V,dz/V);
    } else return MPoint.New();
  }

  public MPoint findGrad_i3(MPoint x)
  {
    double dx, dy, dz;
    double dxdx, dydy, dzdz;
    double V;

    dx = p.x-x.x;
    dy = p.y-x.y;
    dz = p.z-x.z;

    dxdx = dx*dx;
    dydy = dy*dy;
    dzdz = dz*dz;

    V = dxdx+dydy+dzdz;
    V = Math.sqrt(V*V*V*V*V*V*V*V*V);
    V = Math.sqrt(Math.sqrt(V)); // V = V^9/8

    if (V>1E-30)
    {
      V=V*V*2; // incorporationg 1/2
      return MPoint.New(dx/V,dy/V,dz/V);
    } else return MPoint.New();
  }

  public void moveGrad(MPoint x, MPoint s) { s.move(findGrad(x)); };
  public void moveGrad_i2(MPoint x, MPoint s) { s.move(findGrad_i2(x)); };
  public void moveGrad_i3(MPoint x, MPoint s) { s.move(findGrad_i3(x)); };

  /** Find the gradient, responsible to the parallel current. */
  public void moveGrad_c(MPoint x, MPoint s) { return; }

  /** Find the gradient, responsible to the parallel current. */
  public void moveGrad_d(MPoint x, MPoint s) { return; }

}

SectFieldFinder.java
--------------------------------------------------------------

package fungimedia_3d;

/**
 * <p>Finds the line of the section.</p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public class SectFieldFinder implements FieldFinder {

  public int getSid() { return sid; };

  public static final double ERR = 1E-5;

  public double findField(MPoint p)
  {
    double f1,f2;

    double l1 = a.distanceToSQ(p);
    double l2 = b.distanceToSQ(p);
    if (l1==0 || l2==0) return 0;

    f1 = (1/l1+1/l2)*0.5;
    double lm = mid.distanceToSQ(p);
    if (lm==0) return 0;
    f2 = (1/l1 + 1/l2 + 1/lm)/3;

    double f0 = -1;

    int n = 2;
    while (abs(f2-f1)>err &&
           (f0>=0 && abs(f1-f0)>abs((f2-f1))) &&
           n<200)
     {
       n*=2;
       f0 = f1;
       f1 = findFieldScan(p,n);
       f2 = findFieldScan(p,n+1);
     }

    f0 = f2*lcu;
    if (f0>Integer.MAX_VALUE) return 0; // block if very close

    return f0;
  }

  public void moveGrad(MPoint p, MPoint S)
  {
    double l = a.distanceTo(b)/chargeUnitLength;
    if (l<1E-30) return;

     final MPoint f1 = MPoint.New();
     final MPoint f2 = MPoint.New();
     MPoint f0 = null;

     findGradScan(p, 2 ,f1);
     findGradScan(p, 3, f2);

    int n = 2;
    while (
     (f1.distanceToSQ(f2)>err*err) && // error is still large
     (f0!=null && f0.distanceToSQ(f1)>f1.distanceToSQ(f2)) && // error decreases
     (n<200)
          )
           {
             n*=2;
             if (f0==null) f0 = f1.Copy();
                else       f0.accept(f1);
             findGradScan(p,n,f1);
             findGradScan(p,n+1,f2);
           }

    f2.multiply(l);

    S.move(f2);

    f1.free(); f2.free();
  }

  public void moveGrad_i2(final MPoint p, final MPoint S)
  {
    double l = a.distanceTo(b)/chargeUnitLength;
    if (l<1E-30) return;

    final MPoint f1 = MPoint.New();
    final MPoint f2 = MPoint.New();
    MPoint f0 = null;

    findGradScan_i2(p, 2 ,f1);
    findGradScan_i2(p, 3, f2);

    int n = 2;
    while (
     (f1.distanceToSQ(f2)>err*err) && // error is still large
     (f0!=null && f0.distanceToSQ(f1)>f1.distanceToSQ(f2)) && // error decreases
     (n<200)
          )
           {
             n*=2;
             if (f0==null) f0 = f1.Copy();
                else       f0.accept(f1);
             findGradScan_i2(p,n,f1);
             findGradScan_i2(p,n+1,f2);
           }
    if (n>100)
     System.out.println("!"+n);
    f2.multiply(l);

    S.move(f2);

    f1.free(); f2.free();
  }

  /** Old version */
  public void moveGrad_i3(final MPoint p, final MPoint S)
  {
    double l = a.distanceTo(b)/chargeUnitLength;
    if (l<1E-30) return;

    final MPoint f1 = MPoint.New();
    final MPoint f2 = MPoint.New();
    MPoint f0 = null;

    findGradScan_i3(p, 2 ,f1);
    findGradScan_i3(p, 3, f2);

    int n = 2;
    while (
     (f1.distanceToSQ(f2)>err*err) && // error is still large
     (f0!=null && f0.distanceToSQ(f1)>f1.distanceToSQ(f2)) && // error decreases
     (n<200)
          )
           {
             n*=2;
             if (f0==null) f0 = f1.Copy();
                else       f0.accept(f1);
             findGradScan_i3(p,n,f1);
             findGradScan_i3(p,n+1,f2);
           }
    if (n>100)
     System.out.println("!"+n);
    f2.multiply(l);

    S.move(f2);

    f1.free(); f2.free();
  }

  /** Find a field at the point p. */
  private final void findGradScan(MPoint p, int n, MPoint fp)
  {
    double fx=vector.x/n;
    double fy=vector.y/n;
    double fz=vector.z/n;

    double xx = a.x;
    double xy = a.y;
    double xz = a.z;

    fp.x=fp.y=fp.z=0.0;

    double l;

    double dx, dy, dz;
    double V;

    for (int i = 0; i <= n; i++) {

      dx = p.x-xx;
      dy = p.y-xy;
      dz = p.z-xz;

      V = dx*dx+dy*dy+dz*dz;

      if (V!=0)
      {
        V=-0.5*V*V;
        fp.x+= dx/V;
        fp.y+= dy/V;
        fp.z+= dz/V;
      } else
         {
           fp.x=fp.y=fp.z=0.0;
           return;
         }

      xx+=fx;
      xy+=fy;
      xz+=fz;
    }

    n++;

    fp.multiply(lcu/n);
  }

  /** Find a field at the point p (second field interaction).
   * 1/sqrt(R) instead of 1/R^2. */
  private final void findGradScan_i2(MPoint p, int n, MPoint fp)
  {
    double fx=vector.x/n;
    double fy=vector.y/n;
    double fz=vector.z/n;

    double xx = a.x;
    double xy = a.y;
    double xz = a.z;

    fp.x=fp.y=fp.z=0.0;

    double l;

    double dx, dy, dz;
    double V;

    for (int i = 0; i <= n; i++) {

      dx = p.x-xx;
      dy = p.y-xy;
      dz = p.z-xz;

      V = dx*dx+dy*dy+dz*dz;

      if (i2_dir)
       V = Math.sqrt(V*V*V);
       else
       V = Math.sqrt(V*V*V*V*V);
      V = (Math.sqrt(V)); // make even more far distance

      if (V!=0)
      {
        V=-V;
        fp.x+= dx/V;
        fp.y+= dy/V;
        fp.z+= dz/V;
      } else
      {
        fp.x=fp.y=fp.z=0.0;
        return;
      }
      xx+=fx;
      xy+=fy;
      xz+=fz;
    }

    n++;

    fp.multiply(0.5*lcu/n); // 1/2 against the sum sign
  }

  /** Find a field at the point p (second field interaction),
   * ~1/R^6 */
  private final void findGradScan_i3(MPoint p, int n, MPoint fp)
  {
    double fx=vector.x/n;
    double fy=vector.y/n;
    double fz=vector.z/n;

    double xx = a.x;
    double xy = a.y;
    double xz = a.z;

    fp.x=fp.y=fp.z=0.0;

    double l;

    double dx, dy, dz;
    double V;

    for (int i = 0; i <= n; i++) {

      dx = p.x-xx;
      dy = p.y-xy;
      dz = p.z-xz;

      V = dx*dx+dy*dy+dz*dz;

      V = Math.sqrt(V*V*V*V);

      if (V!=0)
      {
        V=-V;
        fp.x+= dx/V;
        fp.y+= dy/V;
        fp.z+= dz/V;
      } else
      {
        fp.x=fp.y=fp.z=0.0;
        return;
      }
      xx+=fx;
      xy+=fy;
      xz+=fz;
    }

    n++;

    fp.multiply(6*lcu/n); // 4 against the sum sign
  }
  /** @todo pointFieldFinder is not updated!!!!! */

  private final MPoint a; // not a copy
  private final MPoint b; // not a copy
  private final double length;

  private final int sid;

  private final MPoint mid; // copy
  private final MPoint vector; // copy
  private final double lcu;
  private final double err;
  private final double chargeUnitLength;
  private final double L;
  private final boolean i2_dir;

  /** Find a field at the point p. */
  private final double findFieldScan(MPoint p, int n)
  {
    MPoint f = vector.Copy();
    f.multiply(1.0/n);

    MPoint x = a.Copy();
    double S=0;
    double l;

    for (int i = 0; i <= n; i++) {
      l = x.distanceToSQ(p);
      if (l==0) return 0; // p is on the |ab|
      S+=1/l;
      x.move(f);
    }

    S = S/(n+1);
    f.free();
    return S;
  }

  /** Create a finder for the section with ends a, b. */
  public SectFieldFinder(Section s, MPoint _a, MPoint _b) {

   sid = s.id;
   a = _a; b = _b;

   chargeUnitLength = s.Options.Field.chargeUnitLength;
   L=a.distanceTo(b);
   mid = MPoint.New( (a.x+b.x)*0.5, (a.y+b.y)*0.5, (a.z+b.z)*0.5 );
   length = a.distanceTo(b);
   lcu = length/chargeUnitLength;
   err = ERR*lcu;
   MPoint v=_b.Copy();
   v.subtract(_a);
   vector = v;
   i2_dir = s.Options.Autotropism_i2.dir_prop;
  }

  private static final double log(double x) { return Math.log(x); };
  private static final double sqrt(double x) { return Math.sqrt(x); };

  private static final double abs(double d)
  {
    if (d>=0) return d;
    return -d;
  }

  /** Find the gradient, responsible to the parallel current.
   * Component for parallel tropisms. */
  public void moveGrad_c(MPoint x, MPoint s)
  {
    if (length<0.01) return;
    // vector, we have.
    MPoint p = vector.Copy();
    double A = x.distanceTo(a);
    double B = x.distanceTo(b);

    double cosa = -(B*B-A*A-length*length)/A/length/2;
    double cosb = -(A*A-B*B-length*length)/B/length/2;

    if (cosa<=0 || cosb<=0) return; // no impact

    lFinal(A, B, p, x, s);
  }

  /** Find the gradient, responsible to the parallel current.
   * Component for positive/negative tropims. */
  public void moveGrad_d(MPoint x, MPoint s)
  {
    if (length<0.01) return;

    double A = x.distanceTo(a);
    double B = x.distanceTo(b);

    double cosa = -(B*B-A*A-length*length)/A/length/2;
    double cosb = -(A*A-B*B-length*length)/B/length/2;

    if (cosa<=0 || cosb<=0) return; // no impact

    double k = A*cosa;

    MPoint p = MPoint.New(
     py(0, length, a.x, b.x, k),
     py(0, length, a.y, b.y, k),
     py(0, length, a.z, b.z, k)
     );

    p.subtract(x);
    lFinal(A,B,p,x,s);
  }

  /** x from x0 to xn, y from y0 to yn, x=px, y=? */
  public static final double py(double x0, double xn, double y0, double yn, double px)
  {
    return ((yn-y0)/(xn-x0))*(px-x0)+y0;
  }

  /** Final steps, shared by moveGrad_c and moveGrad_d. */
  private final void lFinal(double A, double B, MPoint vect, MPoint x, MPoint sum)
  {
    double h = geometry.Triangle.find_h(A,B,length);
    if (Double.isNaN(h)) return;
    if (h<=0) return;
    vect.normalize();
    vect.multiply(1/(h*h));
    sum.move(vect);
    vect.free();
  }

  protected void finalize()
  {
    mid.free();
    vector.free();
  }

}

Section.java
--------------------------------------------------------------

package fungimedia_3d;
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.StringTokenizer;

/** Represents a growing tip of mycelia or non-growing section. */

public class Section extends Line {

  /** Various flags of this section. */
  public int flags = 0;

  public static int LEADING   = 0x1;
  public static int DEAD      = 0x2;
  public static int SECONDARY = 0x4;

  /** Get the value of the given flag. */
  public boolean is(int flag)
  {
    return (flags & flag)!=0;
  }

  /** Set one of the flags. */
  public void set(int flag, boolean state)
  {
    if (state)
      flags |= flag;
      else
      flags &= ~flag;
  }

  /** Pre-computed field, field gradient and field gradient i2 values. Used bu Mycel */
  public fungimedia_3d.Supercomputing.Response pField
   = null; //fungimedia_3d.Supercomputing.Response.n;

  /** The unique id of this section. */
  public final int id;

  /** Get a free unused id. If the mycelia is known, the mycelia set of
   * used ids is checked. Otherwise the default set of unused fields is
   * checked. */
  private synchronized int getFreeId()
  {
    BitSet used;
    if (mycel==null)
      used = used_ids;
      else
      used = mycel.used_ids;

    int i = ids++;
    while (used_ids.get(i)) i++;
    used_ids.set(i, true);
    ids = i+1;
    return i;

  }

  static int ids = 200;
  static final BitSet used_ids = new BitSet();

  /** Id of the base. Only temporary used during loading from XML. */
  public int b_id;

  /** The set of options for this section. */
  public MOptions Options = MOptions.Options;

  /** The orientator for this section. */
  public Orientator orientator = new Orientator(this);

  /** vector (dx and dy) of this tip. */
  public MPoint growth;
  /** The not growing section on mycelia at the base of tip. */
  public Section base;
  /** Branches at the top of this section, if any. */
  public Section[] branches = null;

  public transient double currentDensity = 0;
  transient Mycel mycel = null;

  /** Age of section (the old ones may not grow) */
  public double age = 0;

  static final String LF = "\r\n";

  /** Create a section. */
  public Section()
   {
     id = getFreeId();
   };

   /** Create a section. */
   public Section(int _id)
    {
      id = _id;
      if (used_ids.get(id))
        throw new Error("Multiple id "+id);
      used_ids.set(id, true);
    };

  /** Create section from XML description, assuming that it belongs to
   * the certain mycelium. If there is only one mycelium in the
   * system, null is allowed for _mycelium.
   */
  public Section(Mycel _mycelium, String xml)
  {
   mycel = _mycelium;
   ids+=1000;
   int iid = -1; // expect assignment

    try {
    /*
    <sec i="596" b="392" s="8.0" d="0.03388848683851015" y="37.69 -42.97 127.88" g="-0.1121 -0.2722 0.9557">
      <segs>37.7 -43 127.9,37.3 -43.8 130.7,36.9 -44.9 134.5</segs>
    </sec>
    */
   // our own parsing, no XML driver so far:
   // get all parameter values, knowing that indexOf can be used:
   int s1e = xml.indexOf('>'); // find closing tag
   String header = xml.substring("<sec ".length(), s1e);

   String[] options = header.split("\" ");
   for (int i = 0; i < options.length; i++) {
     options[i]=options[i].trim();
     switch (options[i].charAt(0)) {

       case 'i': iid = getAssignmentInt(options[i]);
         break;
       case 'b': b_id = getAssignmentInt(options[i]);
         break;
       case 's': age = getAssignmentDouble(options[i]);
         break;
       case 'd': currentDensity = getAssignmentDouble(options[i]);
         break;
       case 'y': to = getPointAssignment(options[i]);
         break;
       case 'g': growth = getPointAssignment(options[i]);
         break;
       case 'f': flags =  getAssignmentInt(options[i]);
         break;

       default:
         System.out.println("UNRECOGNISED TOKEN "+options[i]);
         break;
      }
     }
    } catch (Exception ex)
       {
         System.err.println("Unable to parse "+xml);
         ex.printStackTrace();
       }

   // now get the subsegments:
   if (xml.indexOf("<segs>")>=0)
   {
     int a = xml.indexOf("<segs>")+"<segs>".length();
     int b = xml.lastIndexOf("</segs>");
     StringTokenizer st = new StringTokenizer(xml.substring(a,b),",");

     subsegments = new Subsegments(this, st.countTokens());

     while (st.hasMoreTokens())
      subsegments.add(MPoint.New(st.nextToken()));
   }

   BitSet used_Identifiers;
   if (mycel!=null) used_Identifiers = mycel.used_ids;
     else used_Identifiers = used_ids;

   if (used_Identifiers.get(iid))
     throw new Error("Multiple id "+iid);
   used_ids.set(iid, true);
   id = iid;

  }

  public static double getAssignmentDouble(String x) throws NumberFormatException
  {
    int a = x.indexOf("\"");
    return Double.parseDouble(x.substring(a+1));
  }

  public static int getAssignmentInt(String x) throws NumberFormatException
  {
    int a = x.indexOf("\"");
    return Integer.parseInt(x.substring(a+1));
  }

  public static MPoint getPointAssignment(String x)
  {
    x = x.trim();
    if (x.endsWith("\""))
     x = x.substring(0, x.length()-1);
    int a = x.indexOf("\"");

    return MPoint.New(x.substring(a+1));
  }

  /** Return the XML string, representing this section. */
  public StringBuffer toXML()
  {
    StringBuffer b = new StringBuffer();

    b.append("<sec i=\""+id+"\"");
    if (base!=null)
     b.append(" b=\""+base.id+"\"");
     else
     b.append(" b=\"0\"");

    b.append(" s=\""  +age+"\"");

    b.append(" d=\""  +currentDensity+"\"");

    b.append(" y=\""  +to.toString(2)+"\"");
    // from is computed from base.to and is 0 if the base=0

    if (growth!=null)
     b.append(" g=\""+growth.toString(4)+"\"");

    b.append(" f=\""+flags+"\" ");

   b.append(">");

   if (subsegments!=null)
   {
     b.append(LF+"  <segs>");
     for (int i = 0; i < subsegments.size(); i++) {
      b.append( ((MPoint)  subsegments.get(i)).toString(1));
      if (i!=subsegments.size()-1)
       b.append(",");
     }
     b.append("</segs>");
   }
   b.append("</sec>"+LF);

    // Branches are not saved - refilled by scanning "base"

    return b;
  }

  public final static Color dGreen   = new Color(0,128,64);
  public final static Color dBlue    = new Color(0,0,250);
  public final static Color dMagenta = new Color(128,0,128);
  public final static Color dBrown   = new Color(128, 64, 0);
  public final static Color dPink    = new Color(213, 0, 106);

  transient double RootDistance = -1;
  public double getRootDistance()
  {
    if (RootDistance>-0.1) return RootDistance;
    double S = 0;
    Section b = this.base;
    while (b!=null)
    {
      S+=b.length();
      b = b.base;
    }
    RootDistance=S;
    return RootDistance;
  }

  transient int supportedTips = 0;
  /** Count the tips that this section is supporting. */
  public int getNumberOfSupportedTips()
  {
    if (branches==null)
    {
      supportedTips = 1;
    }
    else
    {
      supportedTips = 0;
      for (int i = 0; i < branches.length; i++) {
        supportedTips+=branches[i].getNumberOfSupportedTips();
      }
    }
   return supportedTips;
  }

  static final Color[] vectorColorScale =
      { Color.black,
        dGreen,   // dark green
        dBlue,    // blue
        dMagenta, // magenta
        dBrown,   // brown
        dPink     // dark pink
      };

  static final MPoint VERTICAL = MPoint.New(0,0,1);
  public void paintApex(Graphics g, View v)
  {
    /*
    Point p = v.to2D(to);
    g.drawString(""+(int)(currentDensity*100), p.x, p.y);
    */

    /*
    point or = getOrientationVector();
    double beta = or.angleTo(VERTICAL);
    if (beta>80)
    {
      g.setColor(Color.yellow);
      v.dot(g, to);
      return;
    };
    */

    if (currentDensity<0) return;
    if (!v.show_tips) return;
    if (Options.Vizualization.show_field_vectors)
      {
        double max = getMycel().currentMaximalDensity;
        if (max==0) g.setColor(Color.red);
         else
         {
           max = Math.log(10*max);
           g.setColor(getColorScale(Math.log(10*(max-currentDensity)),  max));
         }

         if (growth!=null)
         if (tropism!=null && tropism.length>1)
         {
           MPoint b = MPoint.New(0,0,0);
           for (int i = 1; i < tropism.length; i++) {
             b.accept(to);
             MPoint m = tropism[i];
             m.normalize();
             m.multiply(7.0);
             b.move(tropism[i]);
             g.setColor(vectorColorScale[i]);
             v.line(g, to, b);
           }
         }

      }
     else
      {
      boolean Grow = currentDensity<Options.Mycelia.growingDensity;
      boolean Branch   = currentDensity<Options.Mycelia.branchingDensity;

      if (Options.Vizualization.greyScale)
       g.setColor(Color.black);
       else
       {
        if (Branch && Grow) g.setColor(Color.red);
         else
        if (Branch) g.setColor(dGreen);
         else
        if (Grow) g.setColor(dBlue);
         else
         g.setColor(Color.black);
        }
      }

    if (growth!=null)
     v.dot(g, to);
  }

  /** Paint this section using root distance coloring. */
  public void paintThisSection_rdc(Graphics g, View v)
  {
    if (!Options.Vizualization.useRootDistanceColoring || is(SECONDARY))
    {
      paintThisSection(g,v);
      return;
    }
    paintApex(g,v);

    if (subsegments==null) drawColoredLine(g, v, from, to, getRootDistance());
     else // this is a curve and not a straight line
     {
       MPoint a = from;
       MPoint b;
       double S = getRootDistance();
       for (int i = 0; i < subsegments.size(); i++) {
         try {
         b = (MPoint) subsegments.get(i);
         drawColoredLine(g,v, a,b, S);
         // v.dot(g,b);
         v.line(g,a,b);
         S+=a.distanceTo(b);
         a=b;
         } catch (ConcurrentModificationException ce) { break; };
       }
       v.line(g, a, to);
      }
  };

  void drawColoredLine(Graphics g, View v, MPoint from, MPoint to, double r)
  {
    double N = length()/24;
    if (N==0) return;

    MPoint p = from.Copy();
    MPoint f = to.Copy();
    f.subtract(p);

    f.multiply(1/N);
    double dr = from.distanceTo(to)/N;
    MPoint t=p.Copy();

    g.setColor(getColorScale(r, mycel.currentMaximalDistanceFromBase));

    for (double i = 0; i < N-1; i++) {
      r+=dr;
      t.move(f);
      v.line(g, p, t);
      //v.dot(g,p);
      p.move(f);
      g.setColor(getColorScale(r, mycel.currentMaximalDistanceFromBase));
    }
    v.line(g,p,to);
  }

  static Color[] rdc;
  static Color[] rdcGrey;
  static {
    rdc = new Color[256];
    rdcGrey = new Color[256];
    int s;
    for (int i = 0; i < rdc.length; i++) {
      s = i % 256;
      rdc[i]=new Color(s, (256-s)/2, s);
      s = s/3;
      rdcGrey[i]=new Color(s, s, s);
    }
  }

  public Color getColorScale(double d, double max)
  {
    if (is(LEADING) || is(DEAD))
    {
      return getSectionColor();
    }
    int c = (int)(rdc.length* d/max);
    // if (c>=rdc.length) c = rdc.length-1;
    c = c % rdc.length;
    if (c<0) c=0;

    if (Options.Vizualization.greyScale) return rdcGrey[c];
     else
      return rdc[c];
  }

  /** Get the section color for the standard mode.
   * By default, it is red for growing section
   * and black for the non-growing.
   */

  public Color getSectionColor()
  {

    if (Options.Vizualization.greyScale)
      {
        if (is(SECONDARY)) return Color.gray;
        return Color.black;
      }

    if (is(DEAD))
     {
       if (postMortal>9) return Color.yellow;
             else        return Color.green;
     }

    if (is(LEADING))
    {
      return Color.blue;
    }
    if (is(SECONDARY)) return Color.RED;

    if (supportedTips>64) return Color.black;
    if (supportedTips>32) return dBrown;
    if (supportedTips>16) return dBlue;
    if (supportedTips>8) return  dGreen;
    if (supportedTips>4) return  dMagenta;
    if (supportedTips>1) return  dPink;
    return Color.red;
  }

  /** Paint this section. */
  public void paintThisSection(Graphics g, View v)
   {
     try {

     paintApex(g,v);
     g.setColor(getSectionColor());

     if (subsegments==null) v.line(g, from,  to );
       else // this is a curve and not a straight line
       {
         MPoint a = from;
         MPoint b;

         for (int i = 0; i < subsegments.size(); i++) {
           try {
           b = (MPoint)subsegments.get(i);
           // v.dot(g,b);
           v.line(g,a,b);
           a=b;
           } catch (ConcurrentModificationException ce)
           {
             ce.printStackTrace();
             break; };
         }
         v.line(g, a, to);
        }

     } catch (Exception exc)
        {
          exc.printStackTrace();
         // happens if the branching thread
         // did not finished initialization of this branch.
        };
   }

  /** Increase the length if growing, no tropisms. */
  private void grow_simple()
   {
     if (currentDensity<Options.Mycelia.growingDensity)
     if (growth!=null)
      {
       double d = getGrowthRate();
       to.x = to.x + d*growth.x;
       to.y = to.y + d*growth.y;
       to.z = to.z + d*growth.z;

       checkGrowthCondition();
      };
   }

   /** Perform minor part of the step, no tropisms, no checks */
   private void advance(double by)
    {
      if (growth!=null)
       {
        double d = getGrowthRate()*by;
        to.x = to.x + d*growth.x;
        to.y = to.y + d*growth.y;
        to.z = to.z + d*growth.z;
       };
    }

   /** Inactivate growth (call stopGrowing) if the conditions are for that. */
   public void checkGrowthCondition()
   {
     if (
        (Options.Mycelia.long_ngrow && length()>Options.Mycelia.maxSectionLength) ||
        (Options.Mycelia.old_ngrow && age>Options.Mycelia.maxGrowingAge))
          {
            stopGrowing();
          }
   }

   /** Get the growth rate that it would be for this section. */
   public double getGrowthRate()
   {
     double v = Options.Mycelia.dGrowth;
     if (!Options.Speed_l_control.suppose)
     {
       MOptions.d_Speed_l_control o = Options.Speed_l_control;
       double ps;
       if (base!=null) return v; // spora
         else ps = 0;
       double l = ps+length();

       double vk = l*Options.Speed_l_control.coeff;

       if (vk>Options.Speed_l_control.maximum)
         return Options.Speed_l_control.maximum;
       return vk;

     }
     else return v;
   }

   /** Get the orientation vector at the apex */
   public MPoint getOrientationVector()
   {
     MPoint b;
     if (subsegments==null)
     {
      if (base!=null)  b = base.to;
        else return null;
     }

     else
        {
          b = (MPoint) subsegments.get(subsegments.size()-1);
          if (b.distanceToSQ(to)<16 && subsegments.size()>1)
            b = (MPoint) subsegments.get(subsegments.size()-2);
        }

     MPoint v = b.Copy();
     v.subtract(to);
     return v;
   }

   /** Get a desired direction of tropism. */
   public MPoint[] getTropismVector()
   {
     return orientator.getTropismVector();
   }

   public MPoint[] tropism = null;

   /** Gradient based growth. */
   public void grow()
    {
      if (growth==null) return;

      if (Options.Mycelia.cbGrowth) // if growth is suppressed by the high field vales
       if (currentDensity > Options.Mycelia.growingDensity) return;

      try {
          tropism = getTropismVector();
          MPoint f = tropism[0];
          if (f.x * f.x + f.y * f.y + f.z * f.z == 0) {
            grow_simple();
            return;
          }

          double d = getGrowthRate();

          try {
          if (subsegments == null || subsegments.size()<2) {
            if (subsegments==null) subsegments = new Subsegments(this);
            subsegments.add(to.Copy());
          } else
             subsegments.advance();
          }
          catch (java.util.ConcurrentModificationException cme) {
          }

          if (f.normalize()) {
              double p = Options.Tropisms.InertiaFactor;
              double q = 1 - p;
              growth.x = p * growth.x + q * f.x;
              growth.y = p * growth.y + q * f.y;
              growth.z = p * growth.z + q * f.z;
              growth.normalize();
            }

          to.x = to.x + d * growth.x;
          to.y = to.y + d * growth.y;
          to.z = to.z + d * growth.z;

          RootDistance=getRootDistance()+d; // inclease the length.

          checkGrowthCondition();

      }
      catch (Exception ex) {
        if (growth!=null)
         ex.printStackTrace();
      }
    }

  /** Array of subsegments, used in the case of the gradient-directed growth. */
  public Subsegments subsegments = null;

  /** Returns true if the tip must branch during this iteration. */
  public boolean willBranchNow()
   {
     if (isDead()) return false;
     if (Options.Mycelia.densityDependend)
      {
       double d_Limit = Options.Mycelia.branchingDensity;

       if (branches!=null && branches.length>0)
       {
        if (branches.length>=Options.Mycelia.maxBranchesFromBranchPoint)
          return false; // too many branches from a single branch point
        // the branch is currently internal
        if (!Options.Mycelia.SecondaryBranching) return false;
         d_Limit = d_Limit*Options.Mycelia.SecondaryBranchingSensAdj;
       }

       boolean v =
         currentDensity<d_Limit &&
         Math.random()<Options.Mycelia.randomComponent;

       // set to false if old does not branch
       if (Options.Mycelia.old_nbranch)
        if (age>Options.Mycelia.maxBranchingAge)
          v = false;
       return v;
      }
     if (branches!=null && !Options.Mycelia.SecondaryBranching) return false;
     return Math.random()<Options.Mycelia.randomComponent;
   }

  /** Branches into branches. By default, a primary (by createPrimaryBranch() ) and
   * secondary (by createBranch() are created. The primary branch gets a copy
   * of the current branch growth vector, and the secondary obtains the vector,
   * returned by getNewGrowingVector(Options.Mycelia.defaultGrowthVector). You need
   * to override this method if the strategy is different, or if more than two branches
   * are created.
   * */
  public void Branch()
   {
     branches = new Section[]
       { createPrimaryBranch(), createBranch() };

     branches[0].mycel=branches[1].mycel=mycel;

     /** Clone the option set. */
     branches[0].Options=branches[1].Options=Options;

     branches[0].to = to.Copy();
     branches[1].to = to.Copy();
     branches[0].from = to;
     branches[1].from = to;
     branches[0].base = branches[1].base = this;
     branches[0].growth = growth;
     branches[1].growth = getNewGrowingVector(Options.Mycelia.defaultGrowthVector);

     branches[0].pField=branches[1].pField=null;
     // stop growing after branching

     branches[0].flags = flags; // primary branch inherits all flags

     branches[1].set(LEADING, Math.random()<Options.Mycelia.prBecomePrimary);

     if (!Options.Field.completeEvaluation
         && !Options.Tropisms.fieldHypothesis)
      branches[0].advance(0.001);
      // needed for Amanita shape. Was not required in the previous versions.
      // It is not completely clear what have changed.
      // I thing it is ok that the new branch forms just behind the current one.

      // primary branch inherits the age of the current branch
      branches[0].age=this.age;

     stopGrowing();
   }

  /** Create a secondary branch when the secion is not already the first in mycelia. */
  public void BranchSecondary()
  {
    if (branches==null) branches = new Section[0];
    Section nb = createBranch();
    nb.to = to.Copy();
    nb.from = to;
    nb.base = this;
    nb.growth = getNewGrowingVector(Options.Mycelia.defaultGrowthVector);
    nb.pField = null;
    nb.mycel = mycel;

    Section[] b = new Section[branches.length+1];
    System.arraycopy(branches,0,b,0,branches.length);
    b[branches.length]=nb;

    nb.set(SECONDARY,true);
    nb.advance(1);

    branches = b;
  }

  public String toString()
   { return super.toString()+" growth "+growth+" length "+length(); };

  private static double length(double x, double y, double z)
   { return x*x+y*y+z*z; };

  final double rd()
  {
    return 0.02*Math.random()+0.99;
  }

  /** Create a branch that continues the growth
   * direction of the current branch during branching. By default,
   * the createBranch()' is returned. It is not required to set the
   * end points, the growth vector, etc. These operations are done
   * in Branch() method.
   */
  public Section createPrimaryBranch()
  {
    return createBranch();
  }

  /** Create a branch. By default, the Options.SectionClass.newInstance()
   * is returned. It is not required to set the
   * end points, the growth vector, mycelia, etc. These operations are done
   * in Branch() method.
   */
  public Section createBranch()
  {
    if (Options.SectionClassName==null)
      return new Section();
    try {
      Class clas = getClass().getClassLoader().loadClass(Options.
          SectionClassName);
      return (Section) clas.newInstance();
    }
    catch (IllegalAccessException ex) {
      throw new Error(ex);
    }
    catch (InstantiationException ex) {
      throw new Error(ex);
    }
    catch (ClassNotFoundException ex) {
      throw new Error(ex);
    }
  }

  /** Get the new growing vector, the angle between this vector and the current growth
   * vector is restrictred to Options.Mycalia.max_Branch_angle */
  public MPoint getNewGrowingVector(double value)
  {
    double max = Options.Mycelia.max_Branch_angle;
    MPoint n = _getNewGrowingVector(value);
    if (growth==null) return n;
    if (growth.lengthSQ()==0) return n;

    try {
      while (growth.angleTo(n) > max) {
        n = _getNewGrowingVector(value);
      }
    }
    catch (Exception ex) {
      return n;
    }

    return n;
  }

  /** Get the new growing vector making no restrictions about its direction. */
  MPoint _getNewGrowingVector(double value)
   {
     if (Options.Mycelia.optimalBranchOrientation)
     {
       MPoint ov = getTropismVector()[0];
       if (ov!=null && ov.normalize())
       {
         ov.x*=rd();
         ov.y*=rd();
         ov.z*=rd();
         ov.multiply(value);
         return ov.Copy();
       }
     }

     double v = value/3;
     double x = v*Math.random();
     double y = v*Math.random();
     double z = v*Math.random();

     double w = value*value;
     while (length(x,y,z)>w)
      { x*=0.9; y*=0.9; z*=0.9; };

     while (length(x,y,z)<w)
      { x*=1.1; y*=1.1; z*=1.1; };

     if (Math.random()<0.5) x = -x;
     if (Math.random()<0.5) y = -y;
     if (Math.random()<0.5) z = -z;

     double r = Math.random();

     if (r<0.33) return MPoint.New(x,y,z); else
     if (r>0.66) return MPoint.New(x,z,y); else
     return MPoint.New(z,x,y);

   }

  public Mycel getMycel()
   {
     return mycel;
   }

  protected boolean active = true;
  protected Collection stableFinders = null;

  /** Add section field finders, specific to this section,
   * to the specified collection of finders.
   */
  public void contributeFinders(Collection finders)
  {
     if (is(DEAD)) return; // dead hyphae does not contribute finders
     // inactive (non-growing) section may just have a set of the stable finders
     // the collection is initialized in stopGrowing.
     if (Options.Field.completeEvaluation)
     {
       if (!isActive())
       {
         if (stableFinders==null)
          { // stable finders are only use for inactive sections under complete field evaluation
            // compute the stable finders:
            stableFinders = new ArrayList();
            contributeAbsoluteFinders(stableFinders);
          }
         finders.addAll(stableFinders);
         return;
       }
        else contributeAbsoluteFinders(finders);
     }
     else
       if (   Options.Field.tips && growth!=null
           || Options.Field.branchPoints && growth==null)
            {
              finders.add(new PointFieldFinder(to, this));
            };
  };

  private void contributeAbsoluteFinders(Collection finders)
   {

    if (subsegments==null || subsegments.size()==0)
    {
     SectFieldFinder f = new SectFieldFinder(this,from,to);
     finders.add(f);
    }
      else // this is a curve and not a straight line
      {
        subsegments.contributeFinders(finders);
       }
  }

  /** True if the section deserves attention,
   * and the method proceed() is worth to call.
   * It is not worthwile to call it for non-branching
   * and non-growing sections. The default implementation
   * returns the value of the variable active,
   * that is initialised to true, and set to false
   * in stopGrowing();
   */
  public boolean isActive()
  {
    return true; // secondary branching is possible
    //return active;
  }

  /** Stop the growth. */
  public void stopGrowing()
  {
    growth = null;
    orientator = null;
    active = false;
  }

  /** Called each time when the internal section
   *  behavior thread should receive control.
   *  The current version returns immediately if
   *  active=false;
   */
  public void proceed()
   {
    setOptionsSetFromFlags();
    // Check destructor
    MOptions.d_Destructor D = Options.Destructor;
    if (D.suppose)
    {
    if (
          (D.hd_Destroy && (pField.field>D.hd) ) ||
          (D.st_Destroy && (supportedTips<D.nst_min) )
        )
         if (!D.l_age_Min || age>D.min_age)
           if (!D.l_age_Max || age<D.max_age)
           {
             if (!(D.l_length_Max || D.l_length_Min))
               die();
               else
               {
                 double len = length();
                 System.out.println("LEN "+len);
                 if (!D.l_length_Max || len<D.max_length)
                   if (!D.l_length_Min || len>D.min_length)
                     die();
                }
           }
    }

     if (is(DEAD)) // The dead sections are still visible for 10 generations.
     {
       postMortal++;
       if (postMortal>10)
       {
         disappear();
         return;
       }
     }

     // check the condition if all daughter branches are dead:
     if (branches!=null && branches.length!=0)
     {
       int dead = 0;
       for (int i = 0; i < branches.length; i++) {
        if (branches[i].isDead()) dead++;
       }
       if (dead==branches.length) die();
     }

     // assume that non growing sections also
     // do not branch.
     //if (!active) return;
     if (mycel==null) return;

     currentDensity = getMycel().Neighbours(to, this);

     grow();

     if (willBranchNow())
     {
       if (growth!=null) Branch();
           else BranchSecondary();
     }
     age+= Options.Mycelia.dAge;
   }

   /** The length of this section. */
   public double length()
   {
     if (subsegments==null)
       return super.length();

     MPoint A = from;
     double S = 0;
     MPoint p;
     Iterator seq = subsegments.iterator();
     while (seq.hasNext()) {
       p = (MPoint)seq.next();
       S+=A.distanceTo(p);
       A=p;
     }
     S+=A.distanceTo(to);
     return S;
   }

  /** Counter to continue displaying the dead branch. */
  int postMortal = 0;

  /** Return true if this section is assumed dead. */
  public boolean isDead() { return is(DEAD); };

  /** Assumes thaqt this section is "dead".
   * The dead sections do not grow and do not
   * contribute any fields.
   */
  public void die()
  {
    if (is(SECONDARY)) return; // secondary immortal
    set(DEAD, true);
    if (branches!=null)
     for (int i = 0; i < branches.length; i++) {
       branches[i].die();
     }
    stopGrowing();
  }

  /** Removes this section and all its daughter sections from the model. */
  public void disappear()
  {
    if (Options.Destructor.keepDeadReferences) return;
    if (base==null) return;
    for (int i = 0; i < base.branches.length; i++) {
      if (base.branches[i]==this)
      {
        // remove from array
        int p = 0;
        Section[] b = new Section[base.branches.length-1];
        for (int k = 0; k < base.branches.length; k++) {
         if (base.branches[k]!=this)
           b[p++]=base.branches[k];
        }
        base.branches = b; // propagate towards the base
        if (base.branches.length==0) // no branches remaining
          base.die();

        return;
      }
    }
    //System.err.println(".branches[] in the parent branch does not containt THIS");
    //throw new Error();
  }

  /** Set the Options variable following the values of the flag registry.
   *  By default, there is a separate set for LEADING and SECONDARY
   * */
  public void setOptionsSetFromFlags()
  {
    if (is(LEADING))
    {
      Options = MOptions.R[1];
    } else
    if (is(SECONDARY))
    {
      Options = MOptions.R[2];
    } else
       Options = MOptions.R[0]; // d_Options.Options
  }

  /** Used as additional data during the advanced rendering with j3d.   */
  public Object RenderingData = null;
}

sliceView.java
--------------------------------------------------------------

package fungimedia_3d;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.util.List;

/**
 * <p> </p>
 * <p>Sight 2.10 System of Internet robots for silicon cloning</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Ulm university</p>
 * @version 2.1.0
 *
 */

public class sliceView extends View implements fungimedia_3d.io.ImageProvider{
  final View mainView;
  public sliceView(View _view) {
    mainView = _view;
    mycelia = mainView.mycelia;
    setOpaque(false);
  }

  Image prev = null;
  public int lTime = -1;

  public void paint(Graphics g) {
    deltaX = mainView.deltaX;
    deltaY = mainView.deltaY;
    alpha = mainView.alpha;
    scale = mainView.scale;

    paintScaleBar(g);

    g.translate(deltaX, deltaY);
    if (mainView.transSectionPlane) paintTransSectionPlane(g);
    g.setColor(Color.black);
    paintCrossingSections(g, mainView.mycelia.base);
  }

  public int slicePlane = 0;

  public static int PLANE_X = 1;
  public static int PLANE_Y = 2;
  public static int PLANE_Z = 4;

  public void setSlicePlane(int plane)
  {
    slicePlane=plane;
    repaint(100);
  }

  public void toggleSlicePlane(int plane)
  {
     setSlicePlane(plane);
  }

  public void paintCrossingSections(Graphics g, Section base)
  {
    if (slicePlane==0) slicePlane = PLANE_Y;
    if ((slicePlane & PLANE_X)!=0)
     {
       paintSubstratesX(g, plane, plane+H);
       paintCrossingSectionsX(g, base);
     }

    if ((slicePlane & PLANE_Y)!=0)
     {
       paintSubstratesY(g, plane, plane+H);
       paintCrossingSectionsY(g, base);
     };

    if ((slicePlane & PLANE_Z)!=0)
     {
      paintSubstratesZ(g, plane, plane+H);
      paintCrossingSectionsZ(g, base);
     }
  }

  public void paintSubstratesX
   (Graphics g, double x0, double xn)
    {
      // paint substrates:
    List substrates = mycelia.substrates;
    if (substrates!=null)
    {
     for (int i = 0; i < substrates.size(); i++) {
       Substrate s = (Substrate) substrates.get(i);

       if (s.center.x+s.R>x0 &&
           s.center.x-s.R<xn )
       s.paint(this,g);
     }
    }
    }

    public void paintSubstratesY
     (Graphics g, double y0, double yn)
      {
        // paint substrates:
      List substrates = mycelia.substrates;
      if (substrates!=null)
      {
       for (int i = 0; i < substrates.size(); i++) {
         Substrate s = (Substrate) substrates.get(i);

         if (s.center.y+s.R>y0 &&
             s.center.y-s.R<yn )
         s.paint(this,g);
       }
      }
      }

      public void paintSubstratesZ
       (Graphics g, double z0, double zn)
        {
          // paint substrates:
        List substrates = mycelia.substrates;
        if (substrates!=null)
        {
         for (int i = 0; i < substrates.size(); i++) {
           Substrate s = (Substrate) substrates.get(i);

           if (s.center.z+s.R>z0 &&
               s.center.z-s.R<zn )
           s.paint(this,g);
         }
        }
        }

  /** x from x0 to xn, y from y0 to yn, x=px, y=? */
  public static final double py(double x0, double xn, double y0, double yn, double px)
  {
    return ((yn-y0)/(xn-x0))*(px-x0)+y0;
  }

  public int H = 10; // Thickness of thranssection

  int H0 = 0; int H1=20;
  /** Transsection over the Y plane (0..y+H. */
  public void paintCrossingSectionsY(Graphics g, Section sec)
  {

    H0 = plane;
    H1 = plane+H;

    double k1 = sec.from.y;
    double k2 = sec.to.y;

    if ((k1>=H0 && k2<=H1) ||
        (k1<=H1 && k2>=H0) )
    {
       sec.paintThisSection_rdc(g, this);
    }

    if (sec.branches!=null && sec.branches.length>0)
    for (int i = 0; i < sec.branches.length; i++) {
      paintCrossingSectionsY(g, sec.branches[i]);
    }

  }

  public Color getLineColor(Section s)
  {
    return s.getSectionColor();
  }

  public void line(Section sec, Graphics g, MPoint a, MPoint b)
  {
    sec.drawColoredLine(g,this ,a,b, sec.getRootDistance());
  }

  /** Transsection over the X plane (0..y+H. */
  public void paintCrossingSectionsX(Graphics g, Section sec)
  {
    H0 = plane;
    H1 = plane+H;

    double k1 = sec.from.x;
    double k2 = sec.to.x;

    if ((k1>=H0 && k2<=H1) ||
        (k1<=H1 && k2>=H0) )
    {
      sec.paintThisSection_rdc(g, this);
    }

    if (sec.branches!=null && sec.branches.length>0)
    for (int i = 0; i < sec.branches.length; i++) {
      paintCrossingSectionsX(g, sec.branches[i]);
    }

  }

  /** Transsection over the Z plane (0..z+H. */
  public void paintCrossingSectionsZ(Graphics g, Section sec)
  {
    H0 = plane;
    H1 = plane+H;

    try {

    double k1 = sec.from.z;
    double k2 = sec.to.z;

    if ((k1>=H0 && k2<=H1) ||
        (k1<=H1 && k2>=H0) )
    {
      sec.paintThisSection_rdc(g, this);
    }
    } catch (Exception exc) {};

    if (sec.branches!=null && sec.branches.length>0)
    for (int i = 0; i < sec.branches.length; i++) {
      paintCrossingSectionsZ(g, sec.branches[i]);
    }

  }

  public void line(Graphics g, MPoint a, MPoint b)
  {
    if (slicePlane==0) slicePlane = PLANE_Y;
    if ((slicePlane & PLANE_X)!=0) lineX(g, a, b);
    if ((slicePlane & PLANE_Y)!=0) lineY(g, a, b);
    if ((slicePlane & PLANE_Z)!=0) lineZ(g, a, b);
  }

  public void dot(Graphics g, MPoint p)
  {
    if (slicePlane==0) slicePlane = PLANE_Y;
    if ((slicePlane & PLANE_X)!=0)
    {
      if (p.x>H0 && p.x<H1) super.dot(g, p);
    } else
    if ((slicePlane & PLANE_Y)!=0)
    {
      if (p.y>H0 && p.y<H1) super.dot(g, p);
    } else
    if ((slicePlane & PLANE_Z)!=0)
    {
      if (p.z>H0 && p.z<H1)
        dotZZ(g,p);
    }
  }

  public void lineX(Graphics g, MPoint a, MPoint b)
  {
    MPoint from, to;

    if (a.x<b.x)
    {
      from = a;
      to   = b;
    }
    else
    {
      to = a;
      from = b;
    }

  if (to.x<H0 || from.x>H1) return; // the line is completely outside

  if ((to.x-from.x)<1E-10) super.line(g, from, to); else
  {
    MPoint f_from = from; MPoint f_to = to;

    if (from.x<H0) // start below the transsection plane
    {
      double z = py(from.x, to.x, from.z, to.z, H0);
      double y = py(from.x, to.x, from.y, to.y, H0);
      f_from = MPoint.New(H0,y,z);
    };

    if (to.x>H1) // end above the transsection plane
    {
      double z = py(from.x, to.x, from.z, to.z, H1);
      double y = py(from.x, to.x, from.y, to.y, H1);
      f_to = MPoint.New(H1,y,z);
    }
    super.line(g, f_from, f_to);
  }
  }

  public void lineY(Graphics g, MPoint a, MPoint b)
  {
        MPoint from, to;

        if (a.y<b.y)
        {
          from = a;
          to   = b;
        }
        else
        {
          to = a;
          from = b;
        }

      if (to.y<H0 || from.y>H1) return; // the line is completely outside

      if ((to.y-from.y)<1E-10) super.line(g, from, to); else
      {
        MPoint f_from = from; MPoint f_to = to;

        if (from.y<H0) // start below the transsection plane
        {
          double z = py(from.y, to.y, from.z, to.z, H0);
          double x = py(from.y, to.y, from.x, to.x, H0);
          f_from = MPoint.New(x, H0, z);
        };

        if (to.y>H1) // end above the transsection plane
        {
          double z = py(from.y, to.y, from.z, to.z, H1);
          double x = py(from.y, to.y, from.x, to.x, H1);
          f_to = MPoint.New(x, H1, z);
        }
        super.line(g, f_from, f_to);
      }
  }

  public void lineZ(Graphics g, MPoint a, MPoint b)
  {
    MPoint from, to;

    if (a.z<b.z)
    {
      from = a;
      to   = b;
    }
    else
    {
      to = a;
      from = b;
    }

  if (to.z<H0 || from.z>H1) return; // the line is completely outside

  if ((to.z-from.z)<1E-10) lineZZ(g, from, to); else
  {
    MPoint f_from = from; MPoint f_to = to;

    if (from.z<H0) // start below the transsection plane
    {
      double x = py(from.z, to.z, from.x, to.x, H0);
      double y = py(from.z, to.z, from.y, to.y, H0);
      f_from = MPoint.New(x,y,H0);
    };

    if (to.z>H1) // end above the transsection plane
    {
      double x = py(from.z, to.z, from.x, to.x, H1);
      double y = py(from.z, to.z, from.y, to.y, H1);
      f_to = MPoint.New(x,y,H1);
    }
    lineZZ(g, f_from, f_to);
  }
  }

  void lineZZ(Graphics g, MPoint a, MPoint b)
   {
    try {
    g.drawLine(
     (int) (scale*(a.x*sin[alpha]+a.z*cos[alpha])),
     (int) (a.y*scale),
     (int) (scale*(b.x*sin[alpha]+b.z*cos[alpha])),
     (int) (b.y*scale)
     );
     } catch (Exception exc)
        {
          System.err.println("a="+a+" and b="+b);
        };
   }

  public Point to2D(MPoint P)
   {
     if (slicePlane!=PLANE_Z)
     {
      return super.to2D(P);
     } else
     {
       return new Point((int) (scale*(P.x*sin[alpha]+P.z*cos[alpha])),
       (int) (P.y*scale));

     }
   }

  void dotZZ(Graphics g, MPoint a)
  {
    g.fillOval(
    (int) (scale*(a.x*sin[alpha]+a.z*cos[alpha]))-2,
    (int) (a.y*scale)-2,4,4);

  }

  static Color[] csc;

  static {
    csc = new Color[250];
    for (int i = 0; i < 25; i++) {
      int f = 255-(i*10);
      csc[i]=new Color(f,f,f);
    }
  }

  public Color getColor(double density)
  {
    int d = (int)Math.round(density);
    if (d>=csc.length) return Color.black;
    return csc[d];
  }

  public void paintScaleBar(Graphics g) {

    int l = (int)(scale*100);
    g.drawLine(10,10,10+l,10);
    g.drawLine(10,5,10,15);
    g.drawLine(l+10,5,l+10,15);
    g.drawString("100 units",10+l/3,30);

    int d = 10+l+20;

    int s = (int)(scale*H);
    g.drawLine(d,10,d+s,10);
    g.drawLine(d,5,d,15);
    g.drawLine(s+d,5,s+d,15);
    g.drawString("Slice thickness, "+H+" units",d+s/3,30);
  }

}

Spora.java
--------------------------------------------------------------

package fungimedia_3d;

/** The basal, initial section of mycelia. */
public class Spora extends Section {

 public Spora(Mycel _mycel)
  {
    super(100);
    //set(LEADING, true);
    mycel = _mycel;
    from = MPoint.New(0,0,0);
    to   = MPoint.New(1,1,1);
    growth = getNewGrowingVector(Options.Mycelia.defaultGrowthVector);
  }

   public String toString()
    { return "Spora: "+super.toString(); }

}

Subsegments.java
--------------------------------------------------------------

package fungimedia_3d;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
/**
 * <p>A chain of points, representing the internal subsegments of the hyphae.
 * For using inside Section. </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public class Subsegments extends FreeableListOfPoints {

  final Section s;
  public Subsegments(Section _s) { s=_s; };
  public Subsegments(Section _s, int size)
  {
    super(size);
    s = _s;
  }

  MPoint last()
  { if (size()==0) return s.from;
    return (MPoint) get(size()-1);
  };

  /** Add the 's.to' point to the end. */
  void extend()
  {
    add(s.to.Copy());
  }

  /** Mind changes, required by the fact that s.to now has a new, different value
   * after a growth step. */
  public void advance()
  {
      MPoint p1 = last();
      MPoint p2 = (MPoint) get(size()-2);
      if (p2.distanceToSQ(s.to)<9) // the point is closer than 3 units.
       p1.accept(s.to);
       else
        if (size()>2)
        {
        {
          MPoint p3 = (MPoint) get(size()-3);
          MPoint m = MPoint.New( (p3.x+s.to.x)*0.5, (p3.y+s.to.y)*0.5, (p3.z+s.to.z)*0.5);
          if (m.distanceToSQ(p2)<9) // distance till the middle point is closer than 3 units
           p1.accept(s.to);
           else extend();
        }
        } else extend();
  }

  transient private ArrayList oldReliable = null;

  public void contributeFinders(Collection finders)
  {
    if (size()<3)
    {
      _contributeFinders(finders);
      return;
    }

    if (oldReliable == null)
    {
      ArrayList f = new ArrayList();
      _contributeFinders(f);
      finders.addAll(f);

      int n = f.size();
      if (n>2)
      {
        // remove the two last: the last point is modified, and the very last finder goes to to.
        f.remove(n-1);
        f.remove(n-2);
        oldReliable = f;
      }
    }
    else
    {
      // the number of finders in oldReliable
      // must math the number of points minus one (last)
      while (oldReliable.size()<size()-1)
      {
        int p = oldReliable.size();
        MPoint a = (MPoint) get(p-1);
        MPoint b = (MPoint) get(p);
        oldReliable.add(new SectFieldFinder(s, a,b));
      }
      finders.addAll(oldReliable);
      MPoint ppl = (MPoint) get(size()-2);
      MPoint ppp = (MPoint) get(size()-1);
      // add the last 2 finders
      finders.add(new SectFieldFinder(s,ppl, ppp));
      finders.add(new SectFieldFinder(s,ppp, s.to));
    }
  }

  /** Contribute all finders, assuming that the this object contains
   * a set of intermediate points between the ends of the section s.
   * Contribute also finders connection to the ends of the section (s.from and s.to).
   */
  private void _contributeFinders(Collection finders)
  {
    MPoint a = s.from;
    MPoint b;
    double S = 0;
    for (int i = 0; i < size(); i++) {
      try {
      b = (MPoint) get(i);
      SectFieldFinder f = new SectFieldFinder(s,a,b);
      finders.add(f);
      a=b;
      } catch (ConcurrentModificationException ce)
      { ce.printStackTrace();
        break; };
    }
    SectFieldFinder f = new SectFieldFinder(s,a,s.to);
    finders.add(f);
  }

}

Substrate.java
--------------------------------------------------------------

package fungimedia_3d;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;

/**
 *  Defines a spherical substrate to demonstrate a positive tropisms.
 */

public class Substrate {

  static int numbers = 1;

  public static synchronized int getFreeNumber()
  {
    return (numbers)++;
  }

  int number = getFreeNumber();

  public double Attractiveness = 0.1;

  public Substrate()
  { this(100,100,-100, 80); };

  public Substrate(double x, double y, double z, double _R)
  {
    center = MPoint.New(x,y,z); R = _R;
  }

  public MPoint center;
  public double R;

  public void paint(View v, Graphics g)
  {
    if (Attractiveness>0) g.setColor(Color.green);
      else g.setColor(Color.pink);
    Point c = v.to2D(center);

    int w = (int) (2*R);
    int cx =(int) (c.x-R);
    int cy =(int) (c.y-R);
    g.fillOval(cx, cy, w,w);

    g.setColor(Color.black);

    g.drawString(""+number, cx, cy);
  }

  public MPoint getGradient(MPoint p)
  {
    double r=center.distanceToSQ(p);
    if (r<R*R) return MPoint.New(0,0,0); // we are inside a substrate, ok

    MPoint g = center.Copy();
    g.subtract(p);
    g.multiply(1/(r*r));
    g.multiply(Attractiveness);
    return g;
  }

  public String toString()
  {
    return number+": "+Attractiveness+", radius "+R+" at "+center;
  }

  public String toXML()
  {
    return "<substrate "+
     "n=\""+number+"\" "+
     "type=\"spheric\" "+
     "a=\""+Attractiveness+"\" "+
     "r=\""+R+"\" "+
     "c=\""+center.toString(2)+"\" "+
     "/>";
  }

  public Substrate(String xml)
  {
    try {
    /*
      <substrate type="spheric" attractiveness="1000.0" radius="20.0" center="100 100 100" />
    */
   // our own parsing, no XML driver so far:
   // get all parameter values, knowing that indexOf can be used:
   int s1e = xml.indexOf('>'); // find closing tag
   String header = xml.substring("<substrate ".length(), s1e);

   String[] options = header.split("\" ");
   for (int i = 0; i < options.length; i++) {
     options[i]=options[i].trim();
     if (options[i].length()>0) // at least a="1"
     switch (options[i].charAt(0)) {
       case 'n': number = getAssignmentInt(options[i]);
         break;
       case 'a': Attractiveness = getAssignmentDouble(options[i]);
         break;
       case 'r': R = getAssignmentDouble(options[i]);
         break;
       case 'c': center = getPointAssignment(options[i]);
         break;
       case 't': // type must be always spheric
         break;
       case '/': break; // just end of the header
       default:
         System.out.println("UNRECOGNISED TOKEN "+options[i]);
         break;
      }
     }
    } catch (Exception ex)
       {
         System.err.println("Unable to parse "+xml);
         ex.printStackTrace();
       }
    System.out.println(toString());
  }

  public static double getAssignmentDouble(String x) throws NumberFormatException
  {
    int a = x.indexOf("\"");
    return Double.parseDouble(x.substring(a+1));
  }

  public static int getAssignmentInt(String x) throws NumberFormatException
  {
    int a = x.indexOf("\"");
    return Integer.parseInt(x.substring(a+1));
  }

  public static MPoint getPointAssignment(String x)
  {
    x = x.trim();
    if (x.endsWith("\""))
     x = x.substring(0, x.length()-1);
    int a = x.indexOf("\"");
    return MPoint.New(x.substring(a+1));
  }

}

Used.java
--------------------------------------------------------------

package fungimedia_3d;

/** Provides the information that kinds of fields are unused and need not be computed. */
public class Used
{
   public static final Used u = new Used();

   public boolean au = true;
   public boolean a2 = true;
   public boolean a3 = true;
   public boolean ac = true;
   public boolean ad = true;

   /** Update options. */
   public void update(Section [] sections, int sl)
   {
     a2 = a3 = ac = au = false;
     MOptions so;
     for (int i = 0; i <= sl; i++) {
       so = sections[i].Options;
       if (so.Autotropism_i2.suppose) a2 = true;
       if (so.Autotropism_i3.suppose) a3 = true;
       if (so.Autotropism_c.suppose)  ac = true;
       if (so.Autotropism_d.suppose)  ad = true;
       if (so.Tropisms.Autotropism)   au = true;

       if (a2 && a3 && ac && au && ad) break; // all used
     }
   }
}

View.java
--------------------------------------------------------------

package fungimedia_3d;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;

/** The basic panel to display 3D view. No scrolls. */
public class View extends JComponent implements fungimedia_3d.io.ImageProvider {

 public Mycel mycelia = Mycel.Mycelia;

 public Mycel getMycelia() { return mycelia; }

 public int deltaX = 0;
 public int deltaY = 0;

 /** Show tips as the colored dots. */
 public boolean show_tips = true;

 static final double [] sin;
 static final double [] cos;

 static
  {
    sin = new double[360];
    cos = new double[360];
    for (int i = 0; i < 360; i++) {
      sin[i] = Math.sin(i*Math.PI/180);
      cos[i] = Math.cos(i*Math.PI/180);
    }
  }

 /** Transsection plane. */
 public int plane = 0;
 public void paintTransSectionPlane(Graphics g)
  {
    /*
    g.setColor(Color.magenta);
    for (int w = 100; w < 400; w+=250) {
      point a1 = new point(-w,plane,-w);
      point a2 = new point(w,plane,-w);
      point a3 = new point(w,plane,w);
      point a4 = new point(-w,plane,w);
      line(g, a1,a2); line(g, a2,a3); line(g, a3,a4); line(g, a4,a1);
      line(g, a1,a3); line(g, a2, a4);
    }
    */
  }

 public BufferedImage getImage()
  {
    return fungimedia_3d.io.ImageSaver.getImage(this);
  }

 public BufferedImage getImage(BufferedImage reuse)
  {
    return fungimedia_3d.io.ImageSaver.getImage(this, reuse);
  }

 public View()
  {
    //setOpaque(true);

    new Thread()
     {
       public void run()
        {
          long last = System.currentTimeMillis();
          long now = last;
          long pause;
          while(true)
           {
              if (mycelia.allDead)
              {
                try {
                  Thread.sleep(1000);
                }
                catch (InterruptedException ex) {
                }
              }
              if (!mycelia.pause)
               {
                 last = System.currentTimeMillis();
                 mycelia.proceed();
                 now = System.currentTimeMillis();
                 pause = 300- (now-last);

                 /** Maybe better not to be so nasty... */
                 if (pause<-30*1000 && !web.ParameterPanel.FULL_VERSION)
                 {
                   System.out.println("Not enough processor speed");
                   mycelia.pause=true;
                 }

                 if (pause<100) pause = 100; // let other system resources to run
                 try {
                   Thread.sleep(pause);
                 }
                 catch (InterruptedException ex) {
                 }

                 repaint();

               } else
                 try {
                   Thread.sleep(100);
                 }
                 catch (InterruptedException ex) {
                 }

           }
        }
     }.start();
  }

 public void paintScaleBar(Graphics g)
 {
   g.setColor(Color.black);
   int l = (int)(scale*100);
   int c1= (int) scale;
   g.drawLine(10,10,10+l,10);
   g.drawLine(10,5,10,15);
   g.drawLine(l+10,5,l+10,15);
   if (c1>2)
    g.drawLine(10+c1,5,10+c1,15);
   g.drawString("100 units",10+l/3,30);

 }

 /** Blue x, red y, green z. */
 public void paintCoordinates(Graphics g)
 {
   /*
   g.setColor(Color.blue);
   line(g, new point(), new point(100,0,0));
   g.setColor(Color.red);
   line(g, new point(), new point(0,100,0));
   g.setColor(Color.green);
   line(g, new point(), new point(0,0,100));
   */
 }

 public void paint(Graphics gg)
  {
    /*
    // use buffered image:
    Image buffer = createImage(getWidth(), getHeight());
    Graphics g = buffer.getGraphics();

    paintScaleBar(g);
    g.translate(deltaX, deltaY);

    paintCoordinates(g);
    mycelia.paint(g,this);
    if (transSectionPlane)
     paintTransSectionPlane(g);

    gg.drawImage(buffer, 0,0, getWidth(), getHeight(), this);
    */

   paintScaleBar(gg);
   gg.translate(deltaX, deltaY);

   paintCoordinates(gg);
   mycelia.paint(gg,this);

  };

 public void dot(Graphics g, MPoint a)
   {
    Point p = to2D(a);
    g.fillOval(
     p.x-2,
     p.y-2,
     4,4 );
   }

 public void line(Graphics g, MPoint a, MPoint b)
 {
   Point A = to2D(a);
   Point B = to2D(b);
   g.drawLine(A.x,A.y,B.x,B.y);
 }

 public void setScale(double _scale)
  {
    scale = _scale;
    repaint(100);
  }

 public double scale = 1.0;
 int alpha = 0;

 public boolean transSectionPlane = false;

  /** Convert to 2D co-ordinates, minding both alpha and beta rotations. */
  public Point to2D(MPoint p)
   {
     return new Point(
     (int)
      ((p.x*cos[alpha]-p.y*sin[alpha])*scale),
     (int)
      ((((p.x*sin[alpha]+p.y*cos[alpha])*sin[beta]+p.z*cos[beta]))*scale));
   }

  public int getAlpha() {
    return alpha;
  }

  public void setAlpha(int _alpha) {
    alpha = (_alpha +720) % 360;
    repaint(100);
  }

  int beta = 0;

  public int getBeta() {
    return beta;
  }
  public void setBeta(int _beta) {
    beta = (_beta +720) % 360;
    repaint(100);
  }

}

--------------------------------------------------------------
This concludes the program listing of .java files in subdirectory C:/neighbour-sensing_model/fungi/src/fungimedia_3d
--------------------------------------------------------------

Program listing of 5 .java files in subdirectory C:/neighbour-sensing_model/fungi/src/fungimedia_3d/io (arranged in alphabetical order)
File list follows
ImageProvider.java
ImageSaver.java
ImageSaverAcc.java
MyceliaIO.java
OptionsReader.java

--------------------------------------------------------------

ImageProvider.java
--------------------------------------------------------------

package fungimedia_3d.io;
import java.awt.image.BufferedImage;
/**
 * <p> </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version not attributable
 * @license General public license (GPL)
 */

public interface ImageProvider {

  public BufferedImage getImage();
  public fungimedia_3d.Mycel getMycelia();

}

ImageSaver.java
--------------------------------------------------------------

package fungimedia_3d.io;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileWriter;
import java.util.Date;
import java.util.Iterator;

import javax.swing.JFileChooser;

import fungimedia_3d.MOptions;

public class ImageSaver {

  public static void store(ImageProvider view)
   throws Exception, Error
  {
    MyceliaIO.getSettings();

    String name;
    JFileChooser chooser = new JFileChooser(MyceliaIO.lastImageWritten);
    chooser.setFileFilter(new fungimedia_3d.io.MyceliaIO.mustBeJpegFilter());
    chooser.setDialogTitle("Save the image.");
    chooser.setApproveButtonText("Save image");
    ImageSaverAcc acc = new ImageSaverAcc();
    acc.setFlags(MyceliaIO.accFlags);
    chooser.setAccessory(acc);

    int returnVal = chooser.showOpenDialog(null);
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      name = chooser.getSelectedFile().getAbsolutePath();
      String nl = name.toLowerCase();
      if (!
          (nl.endsWith(".jpg") || nl.endsWith(".jpeg"))
          )
         name = name+".jpg";
    } else return;

    MyceliaIO.accFlags = acc.getFlags();

    store(view, name);

    int k = name.lastIndexOf('.');
    String ns = name.substring(0,k);
    if (acc.cbText.isSelected())
    {
      File doc = new File(ns+".txt");
      FileWriter w = new FileWriter(doc);

      w.write("Mycelia: "+view.getMycelia().getInfo().toString()+MOptions.LF+MOptions.LF);
      w.write(MOptions.DocumentAllText());
      w.close();
    }

    if (acc.cbXML.isSelected())
    {
      File doc = new File(ns+".xml");
      FileWriter w = new FileWriter(doc);
      w.write("<record>"+MOptions.LF);
      w.write("<image src=\""+doc.getAbsolutePath()+"\"/>");
      w.write("<date>"+new Date().toString()+"</date>"+MOptions.LF);
      w.write(MOptions.DocumentAll());
      w.write("</record>"+MOptions.LF);
      w.close();
    }

    if (acc.cbMyc.isSelected())
    {
      MyceliaIO.store(view.getMycelia(), ns+".mycelia.xml");
    }

    MyceliaIO.writeSettings();
  }

  public static void store(ImageProvider v, String name)
   throws Exception, Error
  {

    Iterator writers = javax.imageio.ImageIO.getImageWritersByFormatName("jpg");
    javax.imageio.ImageWriter writer = (javax.imageio.ImageWriter)writers.next();

    javax.imageio.ImageWriteParam para =
         writer.getDefaultWriteParam();

    para.setCompressionMode(para.MODE_DISABLED);

    BufferedImage bi = v.getImage();
    Graphics g = bi.getGraphics();
    g.setColor(Color.black);
    g.drawString("ï¿½ Audrius Meï¿½kauskas 2003", 5, bi.getHeight()-3);

    File f = new File(name);

    javax.imageio.stream.ImageOutputStream ios = javax.imageio.ImageIO.createImageOutputStream(f);
    writer.setOutput(ios);
    writer.write(bi);

    ios.close();

    MyceliaIO.lastImageWritten=
     f.getParent();
    MyceliaIO.writeSettings();
  }

  public static BufferedImage getImage(Component x)
   {
     BufferedImage
      bu = new BufferedImage(x.getWidth(), x.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
      return getImage(x, bu);
   }

  public static BufferedImage getImage(Component x, BufferedImage reuse)
    {
      BufferedImage
       bu = reuse;
      Graphics g = bu.getGraphics();
      g.setColor(Color.white);
      g.fillRect(0,0, bu.getWidth(), bu.getHeight());
      g.setColor(Color.black);
      x.paint(g);
      return bu;
    }

  }

ImageSaverAcc.java
--------------------------------------------------------------

package fungimedia_3d.io;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;

import javax.swing.JCheckBox;
import javax.swing.JLabel;

public class ImageSaverAcc extends javax.swing.JPanel {

  static int PLAIN = 1;
  static int PXML  = 2;
  static int MYCXML = 4;

  JCheckBox cbText = new JCheckBox();
  JCheckBox cbXML = new JCheckBox();
  JCheckBox cbMyc = new JCheckBox();
  JLabel jLabel1 = new JLabel();
  GridBagLayout gridBagLayout1 = new GridBagLayout();

  public ImageSaverAcc() {
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
  private void jbInit() throws Exception {
    jLabel1.setText("Additionally, save:");

    this.setLayout(gridBagLayout1);
    cbText.setSelected(true);
    cbText.setText("Parameters in plain text");
    cbXML.setText("Parameters in xml");
    cbMyc.setText("Whole mycelia in xml");

    this.add(cbText,    new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0));
    this.add(cbXML,    new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0));
    this.add(cbMyc,   new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0));
    this.add(jLabel1,   new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 0, 5), 0, 0));
  }
  public int getFlags() {
    int S = 0;
    if (cbText.isSelected()) S|=PLAIN;
    if (cbXML.isSelected())  S|=PXML;
    if (cbMyc.isSelected())  S|=MYCXML;
    return S;
  }
  public void setFlags(int Flags) {
    cbText.setSelected( (Flags & PLAIN)!=0);
    cbXML.setSelected( (Flags & PXML)!=0);
    cbMyc.setSelected( (Flags & MYCXML)!=0);
  }};

MyceliaIO.java
--------------------------------------------------------------

package fungimedia_3d.io;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;

import javax.swing.JCheckBox;
import javax.swing.JFileChooser;

import fungimedia_3d.MOptions;
import fungimedia_3d.Mycel;
import fungimedia_3d.View;

/**
 * <p> </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public class MyceliaIO {

  public static String lastOpened = "";
  public static String lastWritten = "";
  public static String lastImageWritten = "";
  public static String lastOpenedParameters = "";
  public static int    accFlags=1;

  public static void getSettings()
  {
    try {
      File f = getOptionsFile();
      if (!f.exists()) return;
      FileInputStream in = new FileInputStream(f);
      byte b[] = new byte[ (int) f.length()];
      in.read(b);
      String s = new String(b);

      lastOpened = cutXML(s, "last_opened_m");
      lastWritten = cutXML(s, "last_written_m");
      lastImageWritten = cutXML(s, "last_image_written");
      lastOpenedParameters = cutXML(s, "last_opened_parameters");
      try {
        accFlags = Integer.parseInt(cutXML(s, "accFlags"));
      }
      catch (NumberFormatException ex1) {
      }
    }
    catch (IOException ex) {
      ex.printStackTrace();
    }
  }

  private static File getOptionsFile()
  {
    String homeDir = System.getProperty("user.home");
    String name = homeDir + File.separator + "fungimedia3d.options.xml";
    File f = new File(name);

    return f;
  }

  private static void write(PrintStream p, String tag, String content)
  {
    p.println(" <"+tag+">"+content+"</"+tag+">");
  }

  public static void writeSettings()
  {
    try {
      PrintStream p = new PrintStream(new FileOutputStream(
          getOptionsFile()));
      p.println("<fungimedia_3D v=\"3.0\">");
      write(p,"last_opened_m",          lastOpened);
      write(p,"last_written_m",         lastWritten);
      write(p,"last_image_written",     lastImageWritten);
      write(p,"last_opened_parameters", lastOpenedParameters);
      write(p,"accFlags", ""+accFlags);
      p.println("</fungimedia_3D>");
      p.close();
    }
    catch (FileNotFoundException ex) {
      ex.printStackTrace();
    }
  }

  public static String cutXML(String x, String tag)
  {
    String openTag = "<"+tag+">";
    String closeTag = "</"+tag+">";
    int a = x.indexOf(openTag);
    if (a<0) return "";
    int b = x.indexOf(closeTag, a);
    if (b<0) return "";
    return x.substring(a+openTag.length(), b).trim();
  }

  public static void store(View view)
   throws Exception, Error
  {
    String name = "the computer file system";
    getSettings();
    JFileChooser chooser = new JFileChooser(lastWritten);
    chooser.setApproveButtonText("Save");
    chooser.setApproveButtonToolTipText("Save the model and parameters");
    chooser.setDialogTitle("Save all model data and parameter set.");

    JCheckBox cb = new JCheckBox("Save as image");
    //chooser.setAccessory(cb);
    //chooser.addChoosableFileFilter(new mustBeJpegFilter());

    chooser.addChoosableFileFilter(new mustBeXMLFilter());

    int returnVal = chooser.showOpenDialog(view);
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      name = chooser.getSelectedFile().getAbsolutePath();
      String nl = name.toLowerCase();
      if (!
          (nl.endsWith(".mycelia.xml"))
          )
         name = name+".mycelia.xml";
    } else return;

    store(view.mycelia, name);
    lastWritten=new File(name).getParent();
    writeSettings();
  }

  public static void store(Mycel mycelia, String fileName)
   throws IOException
  {
    PrintStream p = new PrintStream(new FileOutputStream(fileName));
    mycelia.writeXML(p);
    p.close();
  }

  /** Load the options only. */
  public static MOptions loadOptions()
   throws Exception
  {
    String name = "the computer file system";
    getSettings();
    JFileChooser chooser = new JFileChooser(lastOpenedParameters);
    chooser.setApproveButtonText("Read parameters");
    chooser.setApproveButtonToolTipText("Open the previously stored parameters");
    chooser.setFileFilter(new mustBeXMLFilter());
    chooser.setDialogTitle("Load parameters only");

    int returnVal = chooser.showOpenDialog(null);

    if(returnVal == JFileChooser.APPROVE_OPTION) {
      name = chooser.getSelectedFile().getAbsolutePath();
      String nl = name.toLowerCase();
      if (!
          (nl.endsWith(".mycelia.xml"))
          )
         name = name+".mycelia.xml";
    } else return null;

    MOptions options = new MOptions();
    options.setAllValuesFrom(new FileInputStream(name));

    lastOpenedParameters=new File(name).getParent();
    writeSettings();
    return options;
  }

  public static void storeOptions()
   throws Exception, Error
  {
    String name = "the computer file system";
    getSettings();
    JFileChooser chooser = new JFileChooser(lastOpenedParameters);
    chooser.setApproveButtonText("Save parameters");
    chooser.setApproveButtonToolTipText("Save parameters only (not mycelia)");
    chooser.addChoosableFileFilter(new mustBeXMLFilter());

    int returnVal = chooser.showOpenDialog(null);
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      name = chooser.getSelectedFile().getAbsolutePath();
      String nl = name.toLowerCase();
      if (!
          (nl.endsWith(".mycelia.xml"))
          )
         name = name+".mycelia.xml";
    } else return;

    File f = new File(name);
    FileOutputStream out = new FileOutputStream(f);
    PrintStream p = new PrintStream(out);
    p.println(MOptions.DocumentAll());
    p.close();

    lastOpenedParameters=f.getParent();
    writeSettings();
  }

  public static void load(View view)
   throws Exception, Error
  {
    String name = "the computer file system";
    getSettings();
    JFileChooser chooser = new JFileChooser(lastOpened);
    chooser.setApproveButtonText("Open");
    chooser.setApproveButtonToolTipText("Open the previously stored model and parameters");
    chooser.setDialogTitle("Open the previously stored model and parameters");
    chooser.setFileFilter(new mustBeXMLFilter());

    int returnVal = chooser.showOpenDialog(view);

    if(returnVal == JFileChooser.APPROVE_OPTION) {
      name = chooser.getSelectedFile().getAbsolutePath();
      String nl = name.toLowerCase();
      if (!
          (nl.endsWith(".mycelia.xml"))
          )
         name = name+".mycelia.xml";
    } else return;

    // preserve visualisation options

    double scale = MOptions.Options.Vizualization.scale;
    load(view.mycelia, name);
    MOptions.Options.Vizualization.scale = scale;

    lastOpened=new File(name).getParent();
    writeSettings();
  }

  public static void load(Mycel mycelia, String fileName)
   throws IOException
  {
    BufferedReader p = new BufferedReader(new FileReader(fileName));
    mycelia.readXML(p);
    p.close();

    MOptions.setAllValuesFrom(new FileInputStream(fileName));
  }

 public static class mustBeXMLFilter extends javax.swing.filechooser.FileFilter {
  public boolean accept(File f) {
    if (f.isDirectory()) return true;
    if (f.getAbsolutePath().toLowerCase().endsWith(".xml")) return true;
    return false;
  }
  public String getDescription() {
    return "model definition in xml";
  }
 }

 public static class mustBeJpegFilter extends javax.swing.filechooser.FileFilter {
  public boolean accept(File f) {
    if (f.isDirectory()) return true;
    String fap = f.getAbsolutePath().toLowerCase();
    if (fap.endsWith(".jpeg")) return true;
    if (fap.endsWith(".jpg"))  return true;
    return false;
  }
  public String getDescription() {
    return "image of mycelia (.jpeg)"; }
  }

}

OptionsReader.java
--------------------------------------------------------------

package fungimedia_3d.io;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import fungimedia_3d.MOptions;
import fungimedia_3d.MOptions.Chapter;

/**
 * <p>Reads the options (xml)</p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public class OptionsReader {

  // skip till you find the line "parameters"
  static byte P[] = new byte[] {
      '<', 'p', 'a', 'r', 'a', 'm', 'e', 't', 'e', 'r', 's', '>'};

  static byte Pe[] = new byte[] {
       '<', '/', 'p', 'a', 'r', 'a', 'm', 'e', 't', 'e', 'r', 's', '>'};

 /** Find and load the record tag. */
 public static void setAllValuesFrom(MOptions[] R, InputStream input)
  {
    try {
      input = cutParametersSection(input, P, Pe);
    }
    catch (Exception ex2) {
      throw new Error(ex2);
    }

    boolean validation = false;
    boolean ignoreWhitespace = false;
    boolean ignoreComments = false;
    boolean putCDATAIntoText = false;
    boolean createEntityRefs = false;

    Document doc = null;

    // Step 1: create a DocumentBuilderFactory and configure it
    DocumentBuilderFactory dbf =
        DocumentBuilderFactory.newInstance();
    DocumentBuilder db = null;

    try {

      dbf.setValidating(validation);
      dbf.setIgnoringComments(ignoreComments);
      dbf.setIgnoringElementContentWhitespace(ignoreWhitespace);
      dbf.setCoalescing(putCDATAIntoText);
      dbf.setExpandEntityReferences(!createEntityRefs);

      db = dbf.newDocumentBuilder();
      doc = db.parse(input);
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }

    ArrayList subsets = new ArrayList();
    NodeList lsubsets = doc.getElementsByTagName("parameter_subset");
    if (lsubsets==null || lsubsets.getLength()==0)
    {
      // if no subset tag used, just collate all sections as a single parameter set
      NodeList l = doc.getElementsByTagName("section");
      subsets.add(l);
    }
    else
     for (int i = 0; i < lsubsets.getLength(); i++) {
      NodeList l = lsubsets.item(i).getChildNodes();
      if (l!=null)
       subsets.add(l);
     }

    //System.out.println("subsets: "+subsets.size());
    // iterate subsets
    for (int n_si = 0; n_si < subsets.size(); n_si++) {
      NodeList sections = (NodeList) subsets.get(n_si);
      MOptions options = R[n_si];

    // get all sections:
    try {
      for (int i = 0; i < sections.getLength(); i++) {
        Node section = sections.item(i);
        if (!section.getNodeName().equalsIgnoreCase("section"))
        {

          continue;
        }

        String s_name = section.getAttributes().getNamedItem("name").
            getNodeValue();

        boolean supposed = section.getAttributes().getNamedItem("supposed").
            getNodeValue().equalsIgnoreCase("true");

        Chapter c = null;
        try {
          c = (MOptions.Chapter)
              Class.forName("fungimedia_3d.d_Options$" + s_name).newInstance();
        }
        catch (ClassNotFoundException ex3) {
          System.out.println("Section "+s_name+" is deprecated.");
          continue;
        }

        // scan for parameters:
        NodeList para = section.getChildNodes();

        Map parameters = new TreeMap();
        for (int j = 0; j < para.getLength(); j++) {
          Node p = para.item(j);
          if (p.getNodeName().equalsIgnoreCase("parameter"))
          {
            NamedNodeMap attr = p.getAttributes();
            parameters.put(attr.getNamedItem("name").getNodeValue(),
                           attr.getNamedItem("value").getNodeValue());
          }
        }

        // assign the fields:
        // System.out.println(c.getClass().getName());

        Field[] f = c.getClass().getFields();
        String value;
        for (int j = 0; j < f.length; j++) {
          String n = f[j].getName();
          if (n.equals("suppose"))
             value= ""+supposed;
            else
             value = (String) parameters.get(n);
          // System.out.println("    "+n+" is "+value);
          if (value!=null)
          {
            Object d = f[j].get(c);
            if (d instanceof Double)
             f[j].set(c, new Double(value)); else
            if (d instanceof Integer)
             f[j].set(c, new Integer(value)); else
            if (d instanceof String)
             f[j].set(c, value); else
            if (d instanceof Boolean)
             f[j].set(c, new Boolean(value));
             else
               throw new Error("Unsupported type "+d.getClass().getName());
          }
          String f_name = s_name.substring("d_".length()); // remove class prefix
          // now assign c to the corresponding default options field:
          Field cf=
           options.getClass().getField(f_name);
          cf.set(options, c);
        }
      }
    }
    catch (Exception ex1) {
      ex1.printStackTrace();
    }
    }

    // if there are less subsets than free members in an array, propagete the FIRST subset
    if (subsets.size()<R.length)
    {
      for (int i = subsets.size(); i < R.length; i++) {
        R[0].CopyTo(R[i]);
      }
    }
  }

  /** Find and load the record tag. */
 public static InputStream cutParametersSection(InputStream input,
                                                byte[] p, byte[] pe) throws Exception
  {
    try {

      BufferedInputStream in = new BufferedInputStream(input);
      ByteArrayOutputStream b = new ByteArrayOutputStream(2000);

      int c;
      Scan:
      while (true) {
        c = in.read();
        if (c < 0) {
          throw new Exception("<parameters> tag not found.");
        }
        if (c==p[0])
        {
          for (int i = 1; i < p.length; i++) {
            if (p[i]!=in.read())
             continue Scan;
          }
          break Scan;
        }
      }
      // found.
      b.write(p);

      Read:
      while(true)
      {
        c = in.read();
        b.write(c);
        if (c < 0) {
          throw new Error("</parameters> tag not found.");
        }
        if (c==pe[0])
        {
          for (int i = 1; i < pe.length; i++) {
            c = in.read();
            b.write(c);
            if (pe[i]!=c)
             continue Read;
          }
          break Read;
        }
      }
      return new ByteArrayInputStream(b.toByteArray());
    }
    catch (Error ex) {
      ex.printStackTrace();
    }
    catch (IOException ex) {
      ex.printStackTrace();
    }
    throw new Error("Unable to read parameter set.");
  }

}

--------------------------------------------------------------
This concludes the program listing of .java files in subdirectory C:/neighbour-sensing_model/fungi/src/fungimedia_3d/io
--------------------------------------------------------------

Program listing of 7 .java files in subdirectory C:/neighbour-sensing_model/fungi/src/fungimedia_3d/Measure (arranged in alphabetical order)
File list follows
Analyser.java
JGrapher.java
JParaTracker.java
JStatisticsCanvas.java
JTimeLapseCanvas.java
MAspect.java
MPanel.java

--------------------------------------------------------------

Analyser.java
--------------------------------------------------------------

package fungimedia_3d.Measure;

import fungimedia_3d.Mycel;
import fungimedia_3d.Section;

/**
 * <p> </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version not attributable
 * @license General public license (GPL)
 */

public class Analyser {
  Mycel mycelia;
  public Analyser(Mycel m) {
    mycelia = m;
  }

  /** Measures the given parameter. */
  public double[] measure(MAspect a)
  {
    if ((a.flags & MAspect.TIPS_ONLY)>0)
    {
      Section s;

      // count tips
      int tips = 0;
      for (int i = 0; i <= mycelia.sections_length; i++) {
        s=mycelia.sections[i];
        if (!(s.branches!=null && s.branches.length!=0)) tips++;
      }

      double [] l = new double[tips];
      int p = 0;

      for (int i = 0; i <= mycelia.sections_length; i++) {
        s=mycelia.sections[i];
        try {
          if (!(s.branches != null && s.branches.length != 0)) {
            l[p++] = a.measure(s);
          }
        }
        catch (Exception ex) {
        }
      }
      return l;
    }
    else
    {
      double [] l = new double[mycelia.sections_length+1];
      int p = 0;
      Section s;

      for (int i = 0; i <= mycelia.sections_length; i++) {
        s=mycelia.sections[i];
        try {
          l[i] = a.measure(s);
        }
        catch (Exception ex1) {
        }
      }
      return l;
    }
  }
}

JGrapher.java
--------------------------------------------------------------

package fungimedia_3d.Measure;

import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;

import javax.swing.JComponent;

/** Canvas component for the simple mathematical model where just X/Y plot is a result. */

public class JGrapher extends JComponent {
// ******************************************
    public double start_Y = 0;
    public double end_X  = 6;

    public double end_Y   =  360;
    public double initial_Y =  -360;
    public double max_Y_Change = 1;
// ******************************************
    public int n_Steps = 100; // Zingsnius skaicius
    public double rX[]=new double[n_Steps+1]; // X'ai
    public double rY[]=new double[n_Steps+1]; // Y'ai

    public double dX[]=new double[n_Steps+1]; // Duomenu X'ai
    public double dY[]=new double[n_Steps+1]; // Duomenu Y'ai
    public double dE[]=new double[n_Steps+1]; // Duomenu standartines vidurkio paklaidos

    public double xT[] = null; // second (green) line, if present
    public double xY[] = null; // second (green) line, if present

    public int n_Data=0; // Duomenu kiekis

    public boolean fixed_zero = false; // Zero line

    /** Setting to true should cause termination of solution. */
    public boolean IntKey = false;

    public JGrapher()
     {
       setBackground(Color.white);
     };

    // Moralinis grafikos palaikymas:
    private int XMA=120;
    private double smx=1, smy=1, siay=0;
    private int    sdx=0, sdy=0, sry=XMA;
    private FontMetrics Metras;
    private int vx, vy,hx,hy;
    private int SW(String s) { return Metras.stringWidth(s);};

    public int gx(double x) { return (int)Math.round(smx*x)+sdx; };
    public int gy(double y) { return sry-(int)Math.round(smy*(y-siay))+sdy; };

    /** Get data to draw (if present) */
    public boolean getData() // dS - duomenu skaicius skaiciuojnt NUO NULIO
     {  // dS = 0;
        // dX[k]=_d_X; // x value
        // dY[k]=_d_Y; // Y value
        // dE[k]=_d_se; // standard dev
        return false; // true if data are present
     };

  public void paint(Graphics g) {

    n_Steps = rX.length-1;

    int i,j,k,l; // Ciklams
    Metras=this.getFontMetrics(getFont());
    int sLH=Metras.getHeight();
    int LH=sLH*3/2;
    int fX = (LH/8)*2;
    if (fX<2) fX=2;

    // finding X and Y intervals:
    end_Y=initial_Y=rY[0];
    end_X=0;
    for (k=1;k<=n_Steps;k++)
    {
     if (rY[k]>end_Y)   end_Y  =rY[k];
     if (rY[k]<initial_Y) initial_Y=rY[k];
     if (rX[k]>end_X)   end_X=rX[k];
    };

    // takind data into consideration
    if (getData())
     {  for (k=0;k<=n_Data;k++)
      { if (dY[k]>end_Y)   end_Y  =dY[k];
        if (dY[k]<initial_Y) initial_Y=dY[k]; };
     };

    long a=(long)Math.ceil(initial_Y);
    long b=(long) (Math.ceil(end_Y)*1.1);

    long c=(b-a)/2;
    long ak=c>30?30:3;
    long v=(b+a)/2;
    v=(v/ak)*ak; c=(c/ak)*ak;

    c+=ak;

    a=v-c; b=v+c;

    if (fixed_zero) a=0;

    initial_Y=a;
    end_Y=b;

    /****** Drawing axes ***********/
    Rectangle r = getBounds();
    g.setColor(Color.black);
    vx=fX+SW("000000");
    vy=fX; hx=r.width-fX; hy=r.height-2*fX-2*sLH;
    g.drawLine(vx, vy, vx, hy);
    g.drawLine(vx, hy, hx, hy);
    int y=hy, x=vx, dx= ((hx-vx)*95)/(5*100);

    smx = 0.95*(hx-vx)/end_X; sdx=vx;
    for (k=0;k<=5;k++) // Horizontali asis
     { double vT=k*(end_X/5);
       x=gx(vT);
       g.drawLine(x,y-fX,x,y+fX);
       g.drawString(Double.toString(Math.round(vT*10.0)/10.0),x,y+LH);
     };
    g.drawString("Time",(x-vx)/2,hy+2*sLH+fX);
    x=vx;

    double rAngle = end_Y-initial_Y;
    smy = 0.95*(hy-vy)/rAngle; sdy=vy; sry=hy-fX;
    siay = initial_Y;
    double dy = Math.round(rAngle/6);
    int ddT=SW("-120 o");
    int ddL=x-SW("o ");
        for (k=0;k<=6;k++) // Vertikali asis
         { double ly=k*dy+initial_Y;
           y=gy(ly);
           g.drawLine(x-fX,y,x+fX,y);
           g.drawString(Long.toString(Math.round(ly)),x-ddT,y-fX);
           //g.drawString("o",ddL,y-3*fX);
         };

     /****** Drawing model ***********/
     g.setColor(Color.red.darker());
     int xx=gx(rX[0]), yy=gy(rY[0]);
     for (k=1;k<=n_Steps;k++)
      { g.drawLine(xx,yy,xx=gx(rX[k]),yy=gy(rY[k])); };

     /****** Drawing (if present) the second curve. *****/
     if (xT!=null)
      {
         g.setColor(new Color(0,80,0));
         xx=gx(xT[0]); yy=gy(xY[0]);
         for (k=1;k<xT.length;k++)
          { g.drawLine(xx,yy,xx=gx(xT[k]),yy=gy(xY[k])); };
      }

     /****** Drawing data ***********/
     if (getData())
       { g.setColor(Color.blue.darker());
         for (k=0;k<=n_Data;k++)
          { int _ly=gy(dY[k]), _lx=gx(dX[k]), _le=gy(dE[k])-gy(0);
            g.fill3DRect(_lx-fX/2,_ly-fX/2,fX,fX,true);
            g.drawLine(_lx,_ly+_le,_lx,_ly-_le);
          };
       };
      };
}

JParaTracker.java
--------------------------------------------------------------

package fungimedia_3d.Measure;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.text.NumberFormat;
import java.util.ArrayList;

import javax.swing.JComponent;
/**
 * <p> </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 1.0
 * @license General public license (GPL)
 */

public class JParaTracker extends JComponent implements fungimedia_3d.io.ImageProvider {

  public JParaTracker() {
  }

  ArrayList dataLines = new ArrayList();

  double max = 1;
  double min = 0;

  fungimedia_3d.Mycel mycelia;

  void add(double [] h)
  {
    dataLines.add(h);
    dataSetChanged();
  }

  void dataSetChanged()
  {
    double [] line=(double[]) dataLines.get(0);
    max = min = line[0];
    double x;

    for (int i=0; i<dataLines.size(); i++)
    {
      line = (double[]) dataLines.get(i);
      for (int p = 0; p < line.length; p++) {
        x = line[p];
        if (x>max) max = x;
        if (x<min) min = x;
      }
    }
    repaint();
  }

  void reset() { dataLines.clear(); };

  public void paint(Graphics g) {
    if (dataLines.size()==0)
    {
      g.drawString("Data not set",20,20);
      return;
    }

    double [] line;
    double x;
    int gx;

    Rectangle r = getBounds();
    double scale = r.width/(max-min);

    NumberFormat format = NumberFormat.getInstance();
    format.setMaximumFractionDigits(3);

    final int y_offs = 24;

    g.drawString(format.format(min), r.x+5,20);
    g.drawString(format.format((min+max)/2.0), r.x+r.width/2, 20);
    g.drawString(format.format(max), r.x+r.width-50, 20);

    int gy = y_offs;

    for (int i=0; i<dataLines.size(); i++)
    {
      line = (double[]) dataLines.get(i);

      int [] over = new int[(int) Math.ceil(max*scale)+20];

      for (int p = 0; p < line.length; p++) {
        gy = i+y_offs;
        x = line[p];
        gx = (int) Math.round( (x-min)*scale);
        if (gx<0) gx = 0;
        try {
        g.setColor(getColorScale(over[gx]));
          over[gx]++;
        }
        catch (ArrayIndexOutOfBoundsException ex) {
          int [] nOver = new int[2*gx];
          System.arraycopy(over, 0, nOver, 0, over.length);
          over = nOver;
          g.setColor(getColorScale(over[gx]));
          over[gx]++;
        }

        if (pointSize<=1)
         g.drawLine(gx,gy,gx,gy);
        else
         g.drawRect(gx,gy,pointSize,pointSize);
      }
    }
    g.setColor(Color.black);
    g.drawString(header+" (time 0 - "+dataLines.size()+").", r.x+5, gy+y_offs);

  }

  public Color getColorScale(int c)
  {
    try {
      return rdc[c];
    }
    catch (ArrayIndexOutOfBoundsException ex) {
      return Color.red;
    }
  }

  static Color[] rdc;
  static {
    rdc = new Color[257];
    int s;
    rdc[0] = Color.lightGray;
    for (int i = 1; i < rdc.length; i++) {
      rdc[i]= rdc[i-1].darker();
    }
  }

  public void setPointSize(int pointSize) {
    this.pointSize = pointSize;
  }
  public int getPointSize() {
    return pointSize;
  }
  public ArrayList getDataLines() {
    return dataLines;
  }
  public void setDataLines(ArrayList dataLines) {
    this.dataLines = dataLines;
    dataSetChanged();
  }
  public fungimedia_3d.Mycel getMycelia() {
    return mycelia;
  }
  public void setMycelia(fungimedia_3d.Mycel mycelia) {
    this.mycelia = mycelia;
  }

  int pointSize = 0;

  public java.awt.image.BufferedImage getImage()
   {
     BufferedImage
      bu = new BufferedImage(getWidth(), dataLines.size()+70, BufferedImage.TYPE_3BYTE_BGR);
     Graphics g = bu.getGraphics();
     g.setColor(Color.white);
     g.fillRect(0,0, bu.getWidth(), bu.getHeight());
     g.setColor(Color.black);
     paint(g);
     return bu;

   }
   public String getHeader() {
     return header;
   }
   public void setHeader(String header) {
     this.header = header;
   }
   String header;

}

JStatisticsCanvas.java
--------------------------------------------------------------

package fungimedia_3d.Measure;

import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.NumberFormat;

import javax.swing.JComponent;

/** Canvas component to plot statistic distribution. */

public class JStatisticsCanvas extends JComponent implements fungimedia_3d.io.ImageProvider {
// *****************************************

    int n = 17;
    public double initial_Y =  0;
    public double max_Y_Change = 1;
// ******************************************

    public double max_Y = -20;
    public double min_Y = -20;

    public double rX[]; // X'ai
    public double rY[]; // Y'ai

    public boolean fixed_zero = true; // Zero line

    boolean HistogramScaleSet = false;
    double min_X_H = 0;
    double max_X_H = 0;

    boolean X_Scale_Set = false;
    double min_X = 0;
    double max_X = 0;

    int time = 0;

    public JStatisticsCanvas()
     {
       setBackground(Color.white);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     };

    // Graphics support
    int XMA=120;
    double smx=1, smy=1, siay=0, siax=0;
    int    sdx=0, sdy=0, sry=XMA;
    FontMetrics Metras=null;

    int Width(String s) { return Metras.stringWidth(s);};

    int gx(double x) { return (int)Math.round(smx*(x-siax))+sdx; };
    int gy(double y) { return sry-(int)Math.round(smy*(y-siay))+sdy; };

    /** Paint the component */
    public void paint(Graphics g) {

        double start_Y = 0;
        double end_Y   =  100;
        double start_X = 0;
        double end_X  = 6;
        int vx, vy,hx,hy;

        if (rX == null) Histogram();
        if (rX == null)
        {
          g.drawString("Data not set",20,20);
          return;
        }

        int n_Steps = rX.length-1;
        int i,j,k,l; // Ciklams
        if (Metras == null) Metras=this.getFontMetrics(getFont());
        int sLH=Metras.getHeight();
        int LH=sLH*3/2;
        int fX = (LH/8)*2;
        if (fX<2) fX=2;

        // finding X and Y intervals:
        end_Y=initial_Y=rY[0];
        end_X=0;
        for (k=1;k<=n_Steps;k++)
        {
         if (rY[k]>end_Y)     end_Y  =rY[k];
         if (rY[k]<initial_Y) initial_Y=rY[k];
         if (rX[k]>end_X)     end_X=rX[k];
         if (rX[k]<start_X)   start_X=rX[k];
        };

        if (HistogramScaleSet)
        {
          end_X = max_X_H;
          start_X = min_X_H;
        }

        long a=(long)Math.ceil(initial_Y);
        long b=(long) (Math.ceil(end_Y)*1.1);

        long c=(b-a)/2;
        long ak=c>30?30:3;
        long v=(b+a)/2;
        v=(v/ak)*ak; c=(c/ak)*ak;

        c+=ak;

        a=v-c; b=v+c;

        if (fixed_zero) a=0;
        if (max_Y>-1) b = (int) max_Y;

        initial_Y=a;
        end_Y=b;

        siax = start_X;

        /****** Drawing axes ***********/
        Rectangle r = getBounds();
        g.setColor(Color.black);
        vx=fX+Width("000000");
        vy=fX; hx=r.width-fX; hy=r.height-2*fX-2*sLH;
        g.drawLine(vx, vy, vx, hy);
        g.drawLine(vx, hy, hx, hy);

        g.drawString(header, vx+20, 2*sLH);

        int y=hy, x=vx, dx= ((hx-vx)*95)/(5*100);

        smx = 0.95*(hx-vx)/(end_X-start_X); sdx=vx;

        int mod = 1;
        if (n_Steps>6) mod  = 2;
        if (n_Steps>20) mod = 10;
        if (n_Steps>50) mod = 100;
        if (n_Steps>100) mod = 1000;

        for (k=0;k<=n_Steps;k++) // Horizontal axis
         { double vT=rX[k];
           x=gx(vT);
           g.drawLine(x,y-fX,x,y+fX);

           if (k % mod == 0)
            g.drawString(f1(vT),x,y+LH);
         };
        g.drawString(header+", n="+data.length+", mean = "+f3(mean)+", time="+time,(x-vx)/2,hy+2*sLH+fX);
        x=vx;

        double range_y = end_Y-initial_Y;
        smy = 0.95*(hy-vy)/range_y; sdy=vy; sry=hy-fX;
        siay = initial_Y;
        double dy = Math.round(range_y/6);
        int ddT=Width("100.0");

        for (k=0;k<=6;k++) // Vertical axis
         { double ly=k*dy+initial_Y;
           y=gy(ly);
           g.drawLine(x-fX,y,x+fX,y);
           g.drawString(Long.toString(Math.round(ly)),x-ddT,y-fX);
         };

         // Histogram
         g.setColor(Color.red.darker());
         int xx=gx(rX[0]), yy=gy(rY[0]);
         for (k=1;k<=n_Steps;k++)
          {
            g.drawOval(xx-1,yy-1,3,3);
            g.drawLine(xx,yy,xx=gx(rX[k]),yy=gy(rY[k]));
          };
         g.drawOval(xx-1,yy-1,3,3);
     };

  double mean = 0;

  /** Create a histogram */
  public void Histogram()
  {
    if (data==null || data.length==0)
     {
       rX = rY = null;
       return; // data not set
     }

    rX = new double[H_intervals+1];
    rY = new double[H_intervals+1];

    double x_min;
    double x_max;
    double d;

    if (HistogramScaleSet)
    {
      x_min = min_X_H;
      x_max = max_X_H;
    } else
    { x_min = x_max = data[0];
      for (int i = 1; i < data.length; i++) {
        d = data[i];
        if (d>x_max) x_max = d;
        if (d<x_min) x_min = d;
      }
    }

    double w = (x_max-x_min)/H_intervals;

    int n;

    double S = 0;
    double SS = 0;

    for (int i = 1; i < data.length; i++) {
      d = data[i];
      n = (int) ((d-x_min)/w);
      try {
        rY[n] = rY[n] + 1.0;
      }
      catch (ArrayIndexOutOfBoundsException ex) {
        if (n<0) n = 0;
        if (n>=rY.length) n = rY.length-1;
        rY[n] = rY[n] + 1.0;
      }
      S+=d;
      SS+=d*d;
    }

    for (int i = 0; i < rX.length; i++) {
      rX[i] = w*i+x_min+w/2;
      rY[i] = rY[i]*100/data.length;
    }

    mean = S/data.length;

  }

  public double[] getData() {
    return data;
  }

  public void setData(double[] _data, int _time) {
    this.data = _data;
    time = _time;
    Histogram();
    repaint();
  }

  public int getH_intervals() {
    return H_intervals;
  }
  public void setH_intervals(int H_intervals) {
    this.H_intervals = H_intervals;
    Histogram();
    repaint();
  }

   /** Set number of histogram grouping intervals */
   public int H_intervals=10;
   /** Set data to draw */
   public double[] data;
  private void jbInit() throws Exception {
    this.setBackground(Color.white);
  }

  NumberFormat format = NumberFormat.getInstance();
  {  format.setGroupingUsed(false);  };

  String f3(double v)
  {

    format.setMaximumFractionDigits(3);
    return format.format(v);
  };

  String f1(double v)
  {
    format.setMaximumFractionDigits(1);
    return format.format(v);
  }

  public double getMax_Y() {
    return max_Y;
  }
  public void setMax_Y(double max_Y) {
    this.max_Y = max_Y;
  }

  public void setH_Scale(double mn, double mx)
  {
    max_X_H = mx;
    min_X_H = mn;
    HistogramScaleSet = true;

  }

  public void unSetH_Scale() { HistogramScaleSet=false; };

  fungimedia_3d.Mycel mycelia;
  public fungimedia_3d.Mycel getMycelia() {
    return mycelia;
  }
  public void setMycelia(fungimedia_3d.Mycel mycelia) {
    this.mycelia = mycelia;
  }

  public java.awt.image.BufferedImage getImage()
   {
     return fungimedia_3d.io.ImageSaver.getImage(this);
   }

  public String getHeader() {
    return header;
  }
  public void setHeader(String header) {
    this.header = header;
  }

  String header="no header";

  public void setXRange(double min, double max)
  {
    X_bounds_set = true;
    min_X = min;
    max_X = max;
  }

  public void setYRange(double min, double max)
  {
    Y_bounds_set = true;
    min_Y = min;
    max_Y = max;
  }

  boolean Y_bounds_set = false;
  boolean X_bounds_set = false;

  public void unsetBounds()
  {
    X_bounds_set = Y_bounds_set = false;
  }

  public void dropMeasurements(String file)
  {
    if (file == null) return;
    Histogram();

   try {
     PrintStream out = new PrintStream(new FileOutputStream(file));
     for (int i = 0; i < rX.length; i++) {
       out.println(rX[i] + " \t " + rY[i]);
     }
     out.close();
   }
   catch (FileNotFoundException ex) {
     ex.printStackTrace();
   }

  }

}

JTimeLapseCanvas.java
--------------------------------------------------------------

package fungimedia_3d.Measure;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
/**
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version not attributable
 * @license General public license (GPL)
 */

public class JTimeLapseCanvas extends JStatisticsCanvas implements fungimedia_3d.io.ImageProvider {

  {
    fixed_zero = false;
  }

  ArrayList measurements = new ArrayList();

  void addMeasurement(int time, double mean)
  {
    measurements.add(new Measurement(time, mean));
    repaint();
  }

  /** Compute the mean and the standard deviation of mean. */
  public void setData(double[] _data, int _time) {
    if (_data.length==0) return;
    double S = 0;
    for (int i = 0; i < _data.length; i++) {
      S+=_data[i];
    }

    double mean = S/_data.length;

    addMeasurement(_time, mean);

  }

  void reset() { measurements.clear(); };

  class Measurement {
    Measurement(double _time, double _mean)
    { time = _time; mean = _mean; }
    double time;
    double mean;
  }

  int ddT = -1;
  public void paint(Graphics g) {
    if (Metras == null) Metras=this.getFontMetrics(getFont());
    if (ddT<0) ddT=Width("60000.0");

    int n = measurements.size();
    if (n==0)
      {
        g.drawString("Data not set",30,30);
        return;
      }
    rX = new double[n];
    rY = new double[n];
    Measurement m;
    for (int i = 0; i < n; i++) {
      m = (Measurement) measurements.get(i);
      rX[i] = m.time;
      rY[i] = m.mean;
    }

        double start_Y = 0;
        double end_Y   =  100;
        double start_X = 0;
        double end_X  = rX[n-1];
        int vx, vy,hx,hy;

        int n_Steps = rX.length-1;
        int i,j,k,l; // Ciklams

        int sLH=Metras.getHeight();
        int LH=sLH*3/2;
        int fX = (LH/8)*2;
        if (fX<2) fX=2;

        // finding X and Y intervals:
        end_X=start_X = rX[0];
        end_Y=initial_Y=rY[0];

        {
          for (k=1;k<=n_Steps;k++)
          {
           if (rY[k]>end_Y)     end_Y  =rY[k];
           if (rY[k]<initial_Y) initial_Y=rY[k];
           if (rX[k]>end_X)     end_X=rX[k];
           if (rX[k]<start_X)   start_X=rX[k];
          };
        };

        if (X_bounds_set)
        {
          start_X = min_X;
          end_X   = max_X;
        };

        double a, b;
        if (Y_bounds_set)
        {
          a = min_Y;
          b = max_Y;
        }
        else
        {
         a=(long)  Math.floor(initial_Y);
         b=(long) (Math.ceil(end_Y)*1.1);
        };

        long c= (long) (b-a)/2;
        long ak=c>30?30:3;
        double v=(b+a)/2;
        v=(v/ak)*ak; c=(c/ak)*ak;

        c+=ak;

        a=v-c; b=v+c;

        if (fixed_zero) a = 0;

        initial_Y=a;
        end_Y=b;

        siax = start_X;

        /****** Drawing axes ***********/
        Rectangle r = getBounds();
        g.setColor(Color.black);
        vx=fX+10+ddT;
        vy=fX; hx=r.width-fX; hy=r.height-2*fX-2*sLH;
        g.drawLine(vx, vy, vx, hy);
        g.drawLine(vx, hy, hx, hy);

        g.drawString(header, vx+20, 2*sLH);

        int y=hy, x=vx, dx= ((hx-vx)*95)/(5*100);

        smx = 0.95*(hx-vx)/(end_X-start_X); sdx=vx;

        for (double vT=0; vT<end_X; vT+=50) // Horizontal axis
         {
           x=gx(vT);
           g.drawLine(x,y-fX,x,y+fX);
           g.drawString(f1(vT),x,y+LH);
         };

        g.drawString(header+" versus time",(x-vx)/2,hy+2*sLH+fX);
        x=vx;

        double range_y = end_Y-initial_Y;
        smy = 0.95*(hy-vy)/range_y; sdy=vy; sry=hy-fX;
        siay = initial_Y;

        double dy = nScale(range_y/6);

        for (k=0;k<=6;k++) // Vertical axis
         { double ly=k*dy+initial_Y;
           y=gy(ly);
           g.drawLine(x-fX,y,x+fX,y);
           g.drawString(Long.toString(Math.round(ly)),x-ddT,y-fX);
         };

         // Data
         g.setColor(Color.black);
         int xx=gx(rX[0]), yy=gy(rY[0]);
         for (k=1;k<=n_Steps;k++)
          {
            // g.drawOval(xx-1,yy-1,3,3);
            g.drawLine(xx,yy,xx=gx(rX[k]),yy=gy(rY[k]));
          };
         g.drawOval(xx-1,yy-1,3,3);
     };

     public static double nScale(double x)
     {
       double r = 0.001;
       double hx = x/3;
       while (hx>r) r*=10;
       return ( Math.ceil(x/r)*r );
     }

     public void dropMeasurements(String file)
     {
       int n = measurements.size();
       double [] rX = new double[n];
       double [] rY = new double[n];
       Measurement m;
       for (int i = 0; i < n; i++) {
         m = (Measurement) measurements.get(i);
         rX[i] = m.time;
         rY[i] = m.mean;
       }

      try {
        PrintStream out = new PrintStream(new FileOutputStream(file));
        for (int i = 0; i < rX.length; i++) {
          out.println(rX[i] + " \t " + rY[i]);
        }
        out.close();
      }
      catch (FileNotFoundException ex) {
        ex.printStackTrace();
      }

     }

}

MAspect.java
--------------------------------------------------------------

package fungimedia_3d.Measure;
import fungimedia_3d.Section;

public class MAspect
{
  public static final int TIPS_ONLY = 1;
  int flags = 0;
  public MAspect(int _flags) { flags = _flags; };

  public double measure(Section s) { return s.length(); };
}

MPanel.java
--------------------------------------------------------------

package fungimedia_3d.Measure;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import fungimedia_3d.*;
import web.Growth3D;
import java.io.*;

/**
 * <p> </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version not attributable
 * @license General public license (GPL)
 */

public class MPanel extends JPanel implements ActionListener {

  String internode_length = "Length";
  String internode_age    = "Age";
  String internode_orientation =  "Orientation with relate to the vertical";
  String internode_centralAngle = "Orientation with relate to the central axis";

  String f_i1 = "Primary negative autotropism field (log)";
  String f_i2 = "Secondary negative autotropism field";
  String f_i3 = "Tertiary negative autotropism field";
  String f_c  = "Positive parallel tropism field";
  String f_d  = "Negative parallel tropism field";

  String total_length = "Total mycelia length";
  String number_of_tips = "Number of tips";
  String hyphal_growth_unit = "Hyphal growth unit";
  String colony_diameter = "Colony diameter";

  Analyser an;

  Mycel mycelia;
  Growth3D parent = null;

  BorderLayout borderLayout1 = new BorderLayout();
  JTabbedPane jTabs = new JTabbedPane();
  JPanel pnStat = new JPanel();
  BorderLayout borderLayout2 = new BorderLayout();
  JPanel pDiButtons = new JPanel();
  JButton bMeasureNow = new JButton();
  public JStatisticsCanvas jStatistics = new JStatisticsCanvas();
  JTextField taN_int = new JTextField();
  JLabel jLabel1 = new JLabel();
  JTextField max_y = new JTextField();
  JLabel jLabel2 = new JLabel();
  JTextField min_x = new JTextField();
  JLabel jLabel3 = new JLabel();
  JTextField max_x = new JTextField();
  JLabel jLabel4 = new JLabel();
  JScrollPane jScrollPane1 = new JScrollPane();
  JList lMeasureThat = new JList(
      new String[] {
       internode_length,
       internode_age,
       internode_orientation,
       internode_centralAngle,
       total_length,
       number_of_tips,
       hyphal_growth_unit,
       colony_diameter,
       f_i1,
       f_i2,
       f_i3,
       f_c,
       f_d
       });

  public MPanel(Growth3D _parent)
  {
    this();
    parent = _parent;
  }

  public MPanel() {
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
  private void jbInit() throws Exception {
    this.setLayout(borderLayout1);
    pnStat.setLayout(borderLayout2);
    bMeasureNow.setMargin(new Insets(0, 0, 0, 0));
    bMeasureNow.setText("Measure");
    bMeasureNow.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bMeasureNow_actionPerformed(e);
      }
    });
    taN_int.setPreferredSize(new Dimension(40, 20));
    taN_int.setToolTipText("");
    taN_int.setText("10");
    jLabel1.setToolTipText("Set the numer of histogram grouping intervals");
    jLabel1.setText("Intervals:");
    max_y.setPreferredSize(new Dimension(40, 20));
    jLabel2.setToolTipText("Set the fixed maximal y value");
    jLabel2.setText(" Max y:");
    min_x.setPreferredSize(new Dimension(40, 20));
    min_x.setText("");
    jLabel3.setToolTipText("Set the fixed minima X value");
    jLabel3.setText(" Min x");
    max_x.setText("");
    max_x.setPreferredSize(new Dimension(40, 20));
    jLabel4.setToolTipText("Set the fixed maximal X value");
    jLabel4.setText(" Max x");
    lMeasureThat.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    lMeasureThat.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseReleased(MouseEvent e) {
        lMeasureThat_mouseReleased(e);
      }
    });
    jScrollPane1.setPreferredSize(new Dimension(200, 132));
    cb_Tip_only.setToolTipText("Only measure sections that ends by growing hyphal tip");
    cb_Tip_only.setActionCommand("");
    cb_Tip_only.setSelected(true);
    cb_Tip_only.setText("Measure only terminal sections");
    cb_Tip_only.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cb_Tip_only_actionPerformed(e);
      }
    });
    cbParaTrackerActive.setSelected(true);
    cbParaTrackerActive.setText("Activate");
    pnParaTracker.setLayout(borderLayout3);
    cbDoNotMeasure.setToolTipText("Disable measurements to accelerate the computation speed");
    cbDoNotMeasure.setText("Disable measurements");
    jParaTracker.setPreferredSize(new Dimension(1, 4000));
    bReturn.setToolTipText("Return to the initial view of mycelia");
    bReturn.setText("Return");
    bReturn.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bReturn_actionPerformed(e);
      }
    });
    pnParaTracker.setBackground(Color.white);
    jScrollPane2.getViewport().setBackground(Color.white);
    pnStat.setBackground(Color.white);
    bSaveHMImage.setText("Save this image");
    bSaveHMImage.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bSaveHMImage_actionPerformed(e);
      }
    });
    bStatSaveImage.setToolTipText("Save the graph image");
    bStatSaveImage.setMargin(new Insets(0, 0, 0, 0));
    bStatSaveImage.setText("Save image");
    bStatSaveImage.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bStatSaveImage_actionPerformed(e);
      }
    });
    pnTimeCourse.setLayout(borderLayout4);
    jScrollPane3.getViewport().setBackground(Color.white);
    jLabel5.setText(" Max x");
    jLabel5.setToolTipText("Set the fixed maximal X value");
    jLabel6.setText(" Max y:");
    jLabel6.setToolTipText("Set the fixed maximal y value");
    max_y1.setPreferredSize(new Dimension(40, 20));
    min_y1.setText("10");
    min_y1.setToolTipText("");
    min_y1.setPreferredSize(new Dimension(40, 20));
    min_x1.setText("");
    min_x1.setPreferredSize(new Dimension(40, 20));
    max_x1.setPreferredSize(new Dimension(40, 20));
    max_x1.setText("");
    jLabel8.setText(" Min x");
    jLabel8.setToolTipText("Set the fixed minima X value");
    bStatSaveImage1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bStatSaveImage1_actionPerformed(e);
      }
    });
    bStatSaveImage1.setText("Save image");
    bStatSaveImage1.setMargin(new Insets(0, 0, 0, 0));
    jLabel7.setText("Min y");
    cbFixed0.setToolTipText("Force lowest displayed Y value to be 0.0");
    cbFixed0.setSelected(true);
    cbFixed0.setText("Fixed 0");

    bRefreshTimeLapse.setMargin(new Insets(0, 0, 0, 0));
    bRefreshTimeLapse.setText("Refresh");
    bRefreshTimeLapse.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        TimeLapseRepaint(e);
      }
    });
    jButton2.setToolTipText("Save numeric data in the plain text format");
    jButton2.setMargin(new Insets(0, 0, 0, 0));
    jButton2.setText("Save numeric");
    jButton2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jButton2_actionPerformed(e);
      }
    });
    jButton3.setToolTipText("Save numeric data in the plain text format");
    jButton3.setMargin(new Insets(0, 0, 0, 0));
    jButton3.setText("Save numeric");
    jButton3.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jButton3_actionPerformed(e);
      }
    });
    this.add(jTabs, BorderLayout.CENTER);

    pnParaTracker.add(pnParaTrackerControls, BorderLayout.SOUTH);
    pnParaTrackerControls.add(cbParaTrackerActive, null);
    pnParaTrackerControls.add(bSaveHMImage, null);
    pnParaTracker.add(jScrollPane2, BorderLayout.CENTER);
    jScrollPane2.getViewport().add(jParaTracker, null);
    pnStat.add(pDiButtons,  BorderLayout.SOUTH);
    pDiButtons.add(jLabel1, null);
    pDiButtons.add(taN_int, null);
    pDiButtons.add(jLabel2, null);
    pDiButtons.add(max_y, null);
    pDiButtons.add(jLabel3, null);
    pDiButtons.add(min_x, null);
    pDiButtons.add(jLabel4, null);
    pDiButtons.add(max_x, null);
    pDiButtons.add(jButton3, null);
    pDiButtons.add(bStatSaveImage, null);
    pDiButtons.add(bMeasureNow, null);
    pnStat.add(jStatistics, BorderLayout.CENTER);
    pnStat.add(jScrollPane1,  BorderLayout.EAST);
    pnStat.add(jPanel1, BorderLayout.NORTH);
    jPanel1.add(cbDoNotMeasure, null);
    jPanel1.add(cb_Tip_only, null);
    jPanel1.add(bReturn, null);
    jScrollPane1.getViewport().add(lMeasureThat, null);
    pnTimeCourse.add(jScrollPane3,  BorderLayout.CENTER);
    pnTimeCourse.add(pDiButtons1,  BorderLayout.SOUTH);
    pDiButtons1.add(jLabel7, null);
    pDiButtons1.add(min_y1, null);
    pDiButtons1.add(jLabel6, null);
    pDiButtons1.add(max_y1, null);
    pDiButtons1.add(cbFixed0, null);
    pDiButtons1.add(jLabel8, null);
    pDiButtons1.add(min_x1, null);
    pDiButtons1.add(jLabel5, null);
    pDiButtons1.add(max_x1, null);
    pDiButtons1.add(jButton2, null);
    pDiButtons1.add(bStatSaveImage1, null);
    pDiButtons1.add(bRefreshTimeLapse, null);
    jScrollPane3.getViewport().add(jTimeLapseCanvas, null);
    jTimeLapseCanvas.add(jButton1, null);
    lMeasureThat.setSelectedIndex(0);

    jTabs.add(pnStat,  "Distributions");
    jTabs.add(pnTimeCourse,  "Time related");
    jTabs.add(pnParaTracker, "History monitor");

  }

  public Mycel getMycelia() {
    return mycelia;
  }
  public void setMycelia(Mycel m) {
    mycelia = m;
    an = new Analyser(mycelia);
    mycelia.setListener(this);

    jStatistics.setMycelia(m);
    jParaTracker.setMycelia(m);
    jTimeLapseCanvas.setMycelia(m);

    measureNow();
  }

  static final MPoint VERTICAL = MPoint.New(0,0,1);
  JPanel jPanel1 = new JPanel();
  JCheckBox cb_Tip_only = new JCheckBox();
  JCheckBox cbParaTrackerActive = new JCheckBox();
  JPanel pnParaTrackerControls = new JPanel();
  BorderLayout borderLayout3 = new BorderLayout();
  JPanel pnParaTracker = new JPanel();
  JScrollPane jScrollPane2 = new JScrollPane();
  JParaTracker jParaTracker = new JParaTracker();
  JCheckBox cbDoNotMeasure = new JCheckBox();
  JButton bReturn = new JButton();
  JButton bSaveHMImage = new JButton();
  JButton bStatSaveImage = new JButton();
  JPanel pnTimeCourse = new JPanel();
  JScrollPane jScrollPane3 = new JScrollPane();
  BorderLayout borderLayout4 = new BorderLayout();
  public JTimeLapseCanvas jTimeLapseCanvas = new JTimeLapseCanvas();
  JLabel jLabel5 = new JLabel();
  JLabel jLabel6 = new JLabel();
  JTextField max_y1 = new JTextField();
  JTextField min_y1 = new JTextField();
  JTextField min_x1 = new JTextField();
  JTextField max_x1 = new JTextField();
  JLabel jLabel8 = new JLabel();
  JButton bStatSaveImage1 = new JButton();
  JPanel pDiButtons1 = new JPanel();
  JLabel jLabel7 = new JLabel();
  JCheckBox cbFixed0 = new JCheckBox();
  JButton jButton1 = new JButton();
  JButton bRefreshTimeLapse = new JButton();
  JButton jButton2 = new JButton();
  JButton jButton3 = new JButton();

  void bMeasureNow_actionPerformed(ActionEvent e)
  {
    try {
      measureNow();
    }
    catch (Exception ex) {
      System.out.println("Cannot measure mycelia at the current moment.");
    }
  }

  private static final double sqr(double x) { return x*x; };

  public void measureNow()
  {

    if (fungimedia_3d.Main.headless)
     return; // statistics is currently only availabe via GUI

    double [] l = null;

    String m = (String) lMeasureThat.getSelectedValue();

    jStatistics.setHeader(m);
    jParaTracker.setHeader(m);
    jTimeLapseCanvas.setHeader(m);

    int flags = 0;
    if (cb_Tip_only.isSelected())
     flags |= MAspect.TIPS_ONLY;

    if (m.equals(internode_length))
    l=an.measure(new MAspect(flags) {
     public double measure(Section s) { return s.length(); } });

    if (m.equals(internode_age))
     l=an.measure(new MAspect(flags) {
      public double measure(Section s) { return s.age; } });

    if (m.equals(internode_orientation))
    l=an.measure(new MAspect(flags) {
    public double measure(Section s)
    {
      MPoint or = s.getOrientationVector();
      return or.angleTo(VERTICAL);
    } }
    );

    if (m.equals(internode_centralAngle))
    l=an.measure(new MAspect(flags) {
    public double measure(Section s)
    {
      double r = sqr(s.to.x-s.from.x)+sqr(s.to.y-s.from.y);

      double sg;
      if (sqr(s.to.x)+sqr(s.to.y) > sqr(s.from.x)+sqr(s.from.y)) sg = -1;
        else sg = 1;

      return 180+180*Math.atan2(s.to.z-s.from.z, sg*Math.sqrt(r))/Math.PI;
    } }
    );

    if (m.equals(colony_diameter))
    {
      l = new double[] { measureRadius(getMycelia()) };
    }

    if (m.equals(f_i1))
    l=an.measure(new MAspect(flags) {
    public double measure(Section s)
    {
      if (s.currentDensity<0.0001) return -4; // limit to 0
      return Math.log(s.currentDensity);
    } }
    );

    if (m.equals(f_i2))
    l=an.measure(new MAspect(flags) {
    public double measure(Section s)
    {
      return s.pField.gradient_i2.length();
    } }
    );

    if (m.equals(f_i3))
    l=an.measure(new MAspect(flags) {
    public double measure(Section s)
    {
      return s.pField.gradient_i3.length();
    } }
    );

    if (m.equals(f_c))
    l=an.measure(new MAspect(flags) {
    public double measure(Section s)
    {
      double f = s.pField.gradient_c.length();
      if (f<0.0001) return -4; // limit to 0
      return Math.log(f);

    } }
    );

    if (m.equals(f_d))
    l=an.measure(new MAspect(flags) {
    public double measure(Section s)
    {
      double f = s.pField.gradient_d.length();
      if (f<0.0001) return -4; // limit to 0
      return Math.log(f);

    } }
    );

    if (m.equals(total_length))
    {
      l=an.measure(new MAspect(flags) {
       public double measure(Section s) { return s.length(); } });
      l = add(l);
    }

    if (m.equals(number_of_tips))
    {
      l=an.measure(new MAspect(flags) {
       public double measure(Section s)
       { return s.growth!=null?1.0:0.0; } });
      l = add(l);
    }

    if (m.equals(hyphal_growth_unit))
    {
      l=an.measure(new MAspect(flags) {
       public double measure(Section s)
       { return s.length(); } });

      double length = add(l)[0];

      l=an.measure(new MAspect(flags) {
       public double measure(Section s)
       { return s.growth!=null?1.0:0.0; } });
      double n = add(l)[0];
      if (length!=0)
       l = new double[] { length/n };
       else
       l = new double[0];
    }

    if (l==null) return;

    if (cbParaTrackerActive.isSelected())
    {
      try {
        jParaTracker.add(l);
      }
      catch (Exception ex1) {
      }
    }

    try {
       jTimeLapseCanvas.setData(l, mycelia.timer);
       setTimeLapseCanvasSettings();
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }

    try {
      int my = Integer.parseInt(max_y.getText());
      jStatistics.setMax_Y(my);
    }
    catch (NumberFormatException ex) {
    }

    try {
      double mn = Integer.parseInt(min_x.getText());
      double mx = Integer.parseInt(max_x.getText());
      jStatistics.setH_Scale(mn, mx);
    }
    catch (NumberFormatException ex) {
      jStatistics.unSetH_Scale();
    }
    jStatistics.setH_intervals(Integer.parseInt(taN_int.getText()));
    jStatistics.setData(l, mycelia.timer);
  }

  private void setTimeLapseCanvasSettings() {
    jTimeLapseCanvas.unsetBounds();

    jTimeLapseCanvas.fixed_zero = cbFixed0.isSelected();

     try {
     jTimeLapseCanvas.setYRange
         (Double.parseDouble(min_y1.getText()),Double.parseDouble(max_y1.getText()));
     } catch (NumberFormatException ex) {
     }

     try {
     jTimeLapseCanvas.setXRange
         (Double.parseDouble(min_x1.getText()),Double.parseDouble(max_x1.getText()));
     } catch (NumberFormatException ex) {
     }
  }

  public void actionPerformed(ActionEvent e) {
    if (fungimedia_3d.Main.headless)
     return; // statistics is currently only availabe via GUI

    if (cbDoNotMeasure.isSelected()) return;
    String cmd = e.getActionCommand();
    if (cmd.equals(Mycel.STEP))
     bMeasureNow_actionPerformed(e);
    if (cmd.equals(Mycel.RESTART))
     {
       reset();
     }
  }

  public void reset()
  {
   jParaTracker.reset();
   jTimeLapseCanvas.reset();
  }

  void cb_Tip_only_actionPerformed(ActionEvent e) {
    bMeasureNow_actionPerformed(e);
  }

  void lMeasureThat_mouseReleased(MouseEvent e) {
    bMeasureNow_actionPerformed(null);
  }

  /** Define this as the next mycelia generation .*/
  public void defineNextGeneration(Mycel m)
  {
    if (fungimedia_3d.Main.headless)
     return; // statistics is currently only availabe via GUI

    try {
      if (getMycelia().timer > m.timer) {
        System.out.println("Timer "+m.timer+" was "+getMycelia().timer+", reset");
        reset();
      }
    }
    catch (Exception ex) {
    }
    setMycelia(m);
  }

  void bReturn_actionPerformed(ActionEvent e) {
   parent.setSelectedCard("model");
  }

  void bSaveHMImage_actionPerformed(ActionEvent e) {
    try {
      fungimedia_3d.io.ImageSaver.store(jParaTracker);
    }
    catch (Error ex) { ex.printStackTrace();      }
    catch (Exception ex) { ex.printStackTrace();  }
  }

  void bStatSaveImage_actionPerformed(ActionEvent e) {
    try {
      fungimedia_3d.io.ImageSaver.store(jStatistics);
    }
    catch (Error ex) { ex.printStackTrace();      }
    catch (Exception ex) { ex.printStackTrace();  }
  }

  static double[] add(double [] l)
  {
    double S = 0;
    for (int i = 0; i < l.length; i++) {
      S+=l[i];
    }
    return new double [] { S } ;
  }

  public void bStatSaveImage1_actionPerformed(ActionEvent e) {
    try {
      fungimedia_3d.io.ImageSaver.store(jTimeLapseCanvas);
    }
    catch (Error ex) { ex.printStackTrace();      }
    catch (Exception ex) { ex.printStackTrace();  }
  }

  void TimeLapseRepaint(ActionEvent e) {
    setTimeLapseCanvasSettings();
    jTimeLapseCanvas.repaint();

  }

  public static double measureRadius(Mycel m)
  {
    double RR = -1;
    Section s;
    int i, j;
    MPoint p;

    for (i = 0; i <= m.sections_length; i++) {
      s = m.sections[i];
      RR = dst(RR, s.to);
      RR = dst(RR, s.from);
      if (s.subsegments!=null)
      {
       for (j=0; i<s.subsegments.size(); i++)
       {
         p = (MPoint) s.subsegments.get(i);
         RR = dst(RR, p);
       }

      }
    }
    if (RR<0) return RR;
    else
     return Math.sqrt(RR);
  }

  static double dst(double RR, MPoint p)
  {
    try {
      double r = sqr(p.x) + sqr(p.y);
      if (r > RR) {
        return r;
      }
      else {
        return RR;
      }
    }
    catch (Exception ex) {
      return RR;
    }
  }

  void jButton2_actionPerformed(ActionEvent e) {
   String file = getFile();
   if (file!=null)
    jTimeLapseCanvas.dropMeasurements(file);
  }

  void jButton3_actionPerformed(ActionEvent e) {
   String file = getFile();
    jStatistics.dropMeasurements(file);
  }

  String prevFile = null;
  String getFile()
  {
    if (prevFile==null)
    {
      fungimedia_3d.io.MyceliaIO.getSettings();
      prevFile = fungimedia_3d.io.MyceliaIO.lastWritten;
    }

    JFileChooser chooser = new JFileChooser(prevFile);
    chooser.setApproveButtonText("Save to this file");
    chooser.setApproveButtonToolTipText("Select the file to store numeric data.");
    chooser.setSelectedFile(new File("p.prn"));

    int returnVal = chooser.showOpenDialog(this);
    if(returnVal == JFileChooser.APPROVE_OPTION)
     {
       prevFile = chooser.getSelectedFile().getParent();
       return chooser.getSelectedFile().getAbsolutePath();
     }
    else
     return null;
  }
}

--------------------------------------------------------------
This concludes the program listing of .java files in subdirectory C:/neighbour-sensing_model/fungi/src/fungimedia_3d/Measure
--------------------------------------------------------------

Program listing of 5 .java files in subdirectory C:/neighbour-sensing_model/fungi/src/fungimedia_3d/Supercomputing (arranged in alphabetical order)
File list follows
CumulativeSectionFieldFinder.java
IsolatedGradientComputator.java
ParallelProcessor.java
Request.java
Response.java

--------------------------------------------------------------

CumulativeSectionFieldFinder.java
--------------------------------------------------------------

package fungimedia_3d.Supercomputing;
import java.util.ArrayList;

import fungimedia_3d.FieldFinder;
import fungimedia_3d.MPoint;
/**
 * <p> A re-usable block of finders. </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public final class CumulativeSectionFieldFinder {

  /** Current section id that must be ignored. */
  public int current_Id = 0;
  final FieldFinder[] finders;

  public CumulativeSectionFieldFinder(ArrayList c_finders)
   {
     finders = new FieldFinder[c_finders.size()];
     for (int i = 0; i < finders.length; i++) {
       finders[i]= (FieldFinder) c_finders.get(i);
     }
   }

   public final double findField(MPoint p, int[] xid)
   {
     double s = 0;
     for (int i = 0; i < finders.length; i++) {
       if (!present(xid, finders[i].getSid()))
        s+= finders[i].findField(p);
     }
     return s;
   }

   public final MPoint findGrad(MPoint p, int[] xid)
   {
     MPoint s = MPoint.New();
     for (int i = 0; i < finders.length; i++) {
       if (!present(xid, finders[i].getSid()))
        finders[i].moveGrad(p, s);
     }
     return s;
   }

   public final MPoint findGrad_i2(MPoint p, int[] xid)
   {
     MPoint s = MPoint.New();
     for (int i = 0; i < finders.length; i++) {
       if (!present(xid, finders[i].getSid()))
        finders[i].moveGrad_i2(p, s);
     }
     return s;
   }

   public final MPoint findGrad_i3(MPoint p, int[] xid)
   {
     MPoint s = MPoint.New();
     for (int i = 0; i < finders.length; i++) {
       if (!present(xid, finders[i].getSid()))
        finders[i].moveGrad_i3(p, s);
     }
     return s;
   }

   public final MPoint findGrad_c(MPoint p, int[] xid)
   {
     MPoint s = MPoint.New();
     for (int i = 0; i < finders.length; i++) {
       if (!present(xid, finders[i].getSid()))
        finders[i].moveGrad_c(p, s);
     }
     return s;
   }

   public final MPoint findGrad_d(MPoint p, int[] xid)
   {
     MPoint s = MPoint.New();
     for (int i = 0; i < finders.length; i++) {
       if (!present(xid, finders[i].getSid()))
        finders[i].moveGrad_d(p, s);
     }
     return s;
   }

   private boolean present(int [] set, int x)
   {
     for (int i = 0; i < set.length; i++) {
       if (set[i]==x) return true;
     }
     return false;
   }
}

IsolatedGradientComputator.java
--------------------------------------------------------------

package fungimedia_3d.Supercomputing;

import fungimedia_3d.MPoint;
import fungimedia_3d.Used;
/**
 * <p> Computes the field, gradient and gradient_i2 for the
 * given set of points using the given gradient finder.</p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public final class IsolatedGradientComputator implements Runnable {

  private final CumulativeSectionFieldFinder finder;
  private final Request[] request;

  public final Response[] response;
  static final Used u = Used.u;

  /** Create computator with this finder, this set of requests. Null
   * values in request array are allowed, the corresponding items
   * in response array will be also null.
   */

  public IsolatedGradientComputator(
      CumulativeSectionFieldFinder _finder, Request[] _request) {
      finder = _finder;
      request = _request;
      response = new Response[request.length];
  }

  public final void run()
  {
    try {
    MPoint au=null, a2=null, a3=null, ac=null, ad=null;
    fungimedia_3d.MPoint Rp;
    double d;
    Request r;

    for (int i = 0; i < request.length; i++) {
      r = request[i];
      if (r!=null) // null requests are expected
      {
        Rp = r.p.Copy();
        d = finder.findField(Rp, r.s_exclude);
        if (u.au)
         au = finder.findGrad(Rp, r.s_exclude);

        if (u.a2)
         a2 = finder.findGrad_i2(Rp, r.s_exclude);

        if (u.a3)
         a3 = finder.findGrad_i3(Rp, r.s_exclude);

        if (u.ac)
         ac  = finder.findGrad_c(Rp, r.s_exclude);

        if (u.ad)
         ad  = finder.findGrad_d(Rp, r.s_exclude);

        response[i]=new Response(d, au, a2, a3, ac, ad);
      }
    }
    }
    catch (Exception exc)
    { exc.printStackTrace(); System.out.flush(); System.exit(667); }
    catch (Error exc)
    { exc.printStackTrace(); System.out.flush(); System.exit(668); }
  }

}

ParallelProcessor.java
--------------------------------------------------------------

package fungimedia_3d.Supercomputing;

import java.util.ArrayList;

import fungimedia_3d.MOptions;
import fungimedia_3d.Section;
/**
 * <p> </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public class ParallelProcessor {

  /** Available number of processors to use. Set 1 for a single processor machine! */
  public static int N_processors = 1;
  /** Time limit for one iteration. */
  public static final int ITERATION_LIMIT = 40*60*1000;

  private static final man.AutoStop autoStop;
  static { autoStop = new man.AutoStop(); autoStop.start(); }

  /** Launch all parallel processes, wait till they terminates. */
  public static final boolean debug = false;

  Response[] pFields;
  Request [] requests;

  /** Process (by calling proceed) all sections
   *  0..sl (end inclusive) in the given array
   *  using the given number of threads.
   */
  public void process(Section[] _sec, int sl, CumulativeSectionFieldFinder finder)
  {
    autoStop.initiate(ITERATION_LIMIT);

    final Section sec[] = new Section[sl+1];
    System.arraycopy(_sec, 0, sec, 0, sec.length);

    pFields = new Response[sl+1];

    // prepare the requests array:
    requests = new Request[pFields.length];
    for (int i = 0; i < pFields.length; i++) {
      // in this version all sections are active.
        {
          Section s = sec[i];
          int [] ex;
          if (s.branches!=null)
          {
           int p = 0;
           ex = new int[s.branches.length+1];
           ex[p++]=s.id;

           for (int bi = 0; bi < s.branches.length; bi++) {
             ex[p++]=s.branches[bi].id;
           }
          } else ex = new int[] { s.id };
          requests[i] = new Request(sec[i].to, ex);
        }
    }

    MOptions.d_Supercomputing computing=
     MOptions.Options.Supercomputing;

    if (N_processors<=1) // single processor machine
    {
      process(0, requests.length, finder); // just process them all
    }
    else
    {  // multiple processors

    int s_pc = sl/N_processors; // sections per processor
    if (s_pc<computing.min_tips_per_processor)
      s_pc = computing.min_tips_per_processor;

    ArrayList processors = new ArrayList();
    Processor p;

    int task_from = 0; // task from, inclusive
    int task_to  =  0; // task to, exclusive
    int inactive =  0; // number of inactive sections, do not mind

    if (debug)
    System.out.println("SECTIONS "+sl+". Sections per processor "+s_pc);

    task_from = 0;
    for (int i = 0; i <=sl; i++) {
      task_to++;

      if (requests[i]==null) inactive++;

      if (task_to-task_from-inactive>=s_pc)
      {
        if (debug)
        System.out.println("Adding task size "+(task_to-task_from));
        p = new Processor(task_from, task_to, finder);
        processors.add(p);
        p.start();
        task_from =task_to;
        inactive = 0;
      }
    }

    if (task_from!=requests.length) // process the remainder in a current thread:
    {
      if (debug)
       System.out.println("Taking self the remainder, size "+(requests.length-task_from));
      process(task_from, requests.length, finder);
    }

    // wait till all terminates:
    boolean done;

    do
    {
      done=true;

      for (int i = 0; i < processors.size(); i++) {
        p = (Processor) processors.get(i);
        if (!p.finished)
        {
          if (debug) System.out.println(p.getName()+" not ready");
          done=false;
          break;
        }
      }
      if (!done)
       try {
         Thread.sleep(200); // sleep for 200 ms if not done
       }
       catch (InterruptedException ex) {
       }
    } while (!done);
    } // end multiple processors

    // fill-in pre-computed field value records for sections:
    for (int i = 0; i < pFields.length; i++) {
      sec[i].pField = pFields[i];
      sec[i].proceed();
    }

    autoStop.OK();
  }

  /** Process the subsection, fill the corresponsing part of responses.
   *  Is called from Processor as the outer class method.
   */
  public void process(int from, int to_exclusive, CumulativeSectionFieldFinder finder)
  {
    if (debug)
     System.out.println("Process "+from+".."+(to_exclusive-1));

    int n = to_exclusive-from;
    Request[] r = new Request[n];
    int p = 0;
    for (int i = from; i < to_exclusive; i++) {
      r[p++]=requests[i];
    }

    IsolatedGradientComputator ico =
     new IsolatedGradientComputator(finder, r);

    try {
    ico.run();
    }
    catch (Exception ex)
    { System.err.println("Error in ico");
      ex.printStackTrace(); };

    Response[] ats = ico.response;
    p = 0;
    for (int i = from; i < to_exclusive; i++) {
     pFields[i]=ats[p++];
    }

  }

 class Processor extends Thread {

   Processor (int _from, int _to_ex, CumulativeSectionFieldFinder _finder)
   {
     from = _from;
     to_ex = _to_ex;
     finder = _finder;
   }

   public static final boolean debug = ParallelProcessor.debug;

   boolean started = false;
   boolean finished = false;

   final int from;
   final int to_ex;
   final CumulativeSectionFieldFinder finder;

   public void run() {
     if (debug)
       System.out.println(from+".."+(to_ex-1)+ " started.");

     started = true;
     process(from, to_ex, finder);

     finished = true;
     if (debug)
       System.out.println(from+".."+(to_ex-1)+ " finished.");

   }
 }
 }

Request.java
--------------------------------------------------------------

package fungimedia_3d.Supercomputing;
import fungimedia_3d.MPoint;
/**
 * <p>Request (normally derived from Section) to compute the fields. </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public final class Request {

  /** Section number to exclude. */
  public final int[] s_exclude;
  /** Point to compute the field for. */
  public MPoint p;

  public Request(MPoint _p, int[] excluding_sections) {
    p = _p;
    s_exclude = excluding_sections;
  }

}

Response.java
--------------------------------------------------------------

package fungimedia_3d.Supercomputing;
import fungimedia_3d.MPoint;
/**
 * <p>Response for isolatedGradientComputator</p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public final class Response {

  public final double field;
  public final MPoint gradient;
  public final MPoint gradient_i2;
  public final MPoint gradient_i3;
  public final MPoint gradient_c;
  public final MPoint gradient_d;

  public Response(
      double _field,
      MPoint _gradient,
      MPoint _gradient_i2,
      MPoint _gradient_i3,
      MPoint _gradient_c,
      MPoint _gradient_d
      ) {
       field = _field;
       gradient = _gradient;
       gradient_i2 = _gradient_i2;
       gradient_i3 = _gradient_i3;
       gradient_c  = _gradient_c;
       gradient_d  = _gradient_d;
  }

  public static final Response n =
   new Response(0,
    MPoint.New(0,0,0), MPoint.New(0,0,0), MPoint.New(0,0,0), MPoint.New(0,0,0), MPoint.New(0,0,0) );

}

--------------------------------------------------------------
This concludes the program listing of .java files in subdirectory C:/neighbour-sensing_model/fungi/src/fungimedia_3d/Supercomputing
--------------------------------------------------------------

Program listing of one .java file in subdirectory C:/neighbour-sensing_model/fungi/src/geometry
Triangle.java

--------------------------------------------------------------

Triangle.java
--------------------------------------------------------------

package geometry;

/** Standard geometrical tasks. */

public class Triangle
{
  /** Find the shortest distance (h) from the point where the
   * sides a and b joins till the side h. See tiangle_find_h.gif in
   * this folder.
   */

  public static final double find_h(double a, double b, double l)
  {
    double bb = b*b;
    double aa = a*a;
    double ll = l*l;
    double t = -bb+aa+ll;
    if (t<=0) return 0;
    t = t*t;
    double t11 = Math.sqrt(4.0-t/aa/ll);
    return a*t11/2;
  }

  /** Find the angle between sides a and l. */
  public static final double find_al_angle(double a, double b, double l)
  {
    double cosa = -(b*b-a*a-l*l)/a/l/2;
    return Math.acos(cosa);
  }

  /** Find the angle between sides a and l. */
  public static final double find_cos_al(double a, double b, double l)
  {
    return -(b*b-a*a-l*l)/a/l/2;
  }

  /** x from x0 to xn, y from y0 to yn, x=px, y=? */
  public static final double py(double x0, double xn, double y0, double yn, double px)
  {
    return ((yn-y0)/(xn-x0))*(px-x0)+y0;
  }

  public static void main(String[] args) {
    System.out.println(find_h(5,6,10));
  }
}

Subdirectory also contains triangle_find_h.GIF
--------------------------------------------------------------

--------------------------------------------------------------
This concludes the listing for subdirectory C:/neighbour-sensing_model/fungi/src/geometry
--------------------------------------------------------------

Program listing of three .java files in subdirectory C:/neighbour-sensing_model/fungi/src/man
File list follows
AutoStop.java
ListOfRecycleables.java
Recycler.java

--------------------------------------------------------------

AutoStop.java
--------------------------------------------------------------

package man;

/**
 * <p>Terminates the application if not switched off after the given time.</p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version not attributable
 * @license General public license (GPL)
 */

public class AutoStop extends Thread {
  long stopAt;
  long interval;
  boolean ok = false;
  boolean r  = false;

  /** Stop the thread, it is ok. */
  public void OK()
  {
    ok = true;
    try {
      interrupt();
    }
    catch (Exception ex) {
    }
    r = false;
  }

  /** Start counting down. The procedure is re-enterable (this will reset a counter). */
  public void initiate(long _maxInterval)
  {
    stopAt   = _maxInterval+System.currentTimeMillis();
    interval = _maxInterval;
    r = true;
    ok = false;
    try {
      interrupt();
    }
    catch (Exception ex) {
    }
  }

  /** Contruct a thread that terminates the program after
   * the specified interval unless OK is called.
   */

  public AutoStop() {
    setPriority(MIN_PRIORITY);
    setDaemon(true);
  }

  public void run()
  {
    while (true)
    {
      // Sleep till the next task is ready
      while (!r)
      {
        try {
          Thread.sleep(30*1000);
        }
        catch (InterruptedException ex) {
        }
      }

      while (ok || System.currentTimeMillis()<stopAt)
      {
        try {
          Thread.sleep(30*1000); // 30 s pause
        }
        catch (InterruptedException ex) {
        }
      }
      if (!ok)
      {
        System.out.println("Step time limit exceeded, terminating after "+interval+" ms");
        System.exit(555);
      } else r = false;
     }
  }

}

ListOfRecycleables.java
--------------------------------------------------------------
package man;

/**
 * <p> List of recycleable components. </p>
 * <p>Copyright: General Public License (GPL)</p>
 * <p>Company: Manchester university</p>
 * @author Audrius Meskauskas
 * @version 1.0
 */

public class ListOfRecycleables {

  public static final boolean NO_EXTEND = true;
  private final int n;
  public ListOfRecycleables(int _n)
  {
    n = _n;
  }

  private Object [] h = new Object[1000];
  private int hp = 0;

  /** Free the object, adding it to this list. The method is not
   * synchronized. Multiple entries may result loss of one or
   * several recycleable objects that will be just collected
   * by gc.
   */
  public final void free(Object x)
  {
    if (hp>=h.length)
    {
      if (NO_EXTEND)
        {
          return;
        }
      else extend();
    }
    h[hp] = x;
    hp++;
  }

  /** This one must be synchronized from outside during the call. */
  public final Object get()
  {
    if (hp==0)
      {
        return null;
      }
    hp--;
    Object o = h[hp];
    h[hp] = null;
    return o;
  }

  final void extend()
  {
    try {
    Object[] hh = new Object[2*h.length];
    System.arraycopy(h,0,hh,0, h.length);
    h = hh;
    }
    catch (OutOfMemoryError oe)
    {
      h = null;
      System.gc();
      System.out.println("Out of memory error in ListOfRecyclaeables.extend; reseting to null");
      h = new Object[100];
      hp = 0;
    }

  }

}

Recycler.java
--------------------------------------------------------------

package man;

/**
 * <p> Class for the thread-safe recycling of the objects.</p>
 */

public class Recycler {

  /** Number of the lists. */
  public static final int N_lists = 1000;

  /** The pointer must point to any (not necessary first of last)
   * member in the list of recycleables.
   */
  final ListOfRecycleables [] lists;

  long seed = 0;
  int random(int n)
  {
    seed = ((seed+12)*7) % Integer.MAX_VALUE;
    return (int) (seed % n);
  }

  public Recycler() {
   lists = new ListOfRecycleables[N_lists];
   for (int i = 0; i < lists.length; i++) {
    lists[i]=new ListOfRecycleables(i);
   }
  }

  /** Free this object. The method is not synchronized. */
  public final void free(Object r)
  {
     ListOfRecycleables l = lists[random(N_lists)];
     synchronized (l)
     {
       l.free(r);
     }

  };

  /** Get some object. The method is not synchronized,
   * but will call the synchronized method in one of the lists. */
  public final Object get()
  {
    Object r;
    ListOfRecycleables l = lists[random(N_lists)];
    synchronized (l)
    {
      r = l.get();
    }
    return r;
  }

  public static void main(String[] args) {
    Recycler r = new Recycler();
    for (int i = 0; i < 100; i++) {
      System.out.println(r.random(10));
    }
  }
}

--------------------------------------------------------------
This concludes the program listing for subdirectory C:/neighbour-sensing_model/fungi/src/man
--------------------------------------------------------------

Program listing of seven .java files in subdirectory C:/neighbour-sensing_model/fungi/src/web
File list follows
AppFrame.java
F3DApplet.java
GraphImage.java
Growth3D.java
NumericField.java
ParameterPanel.java
SubstrateParameterPanel.java

--------------------------------------------------------------

AppFrame.java
--------------------------------------------------------------

package web;

import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;

/**
 * <p> </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version not attributable
 * @license General public license (GPL)
 */

public class AppFrame extends JFrame {

  Growth3D app;

  public AppFrame(Growth3D _app) {
    this();
    app = _app;
    this.getContentPane().add(app);
    setJMenuBar(app.jMenu);
    //add(app);
  }

  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
     if (e.getID() == WindowEvent.WINDOW_CLOSING) {
     try {
       if (app.primary!=this) dispose();
           else System.exit(0);
       }
    catch (Exception sec)
       {
         dispose();
       }
    }
  }
  public synchronized void setTitle(String title) {
    super.setTitle(title);
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
  }

  public AppFrame() {

    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
  private void jbInit() throws Exception {
    setBackground(new Color(215,215,215));
  }

}

F3DApplet.java
--------------------------------------------------------------

package web;

import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;

import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JPanel;

public class F3DApplet extends JApplet {
  private boolean isStandalone = false;
  JPanel jPanel1 = new JPanel();
  JButton bStart = new JButton();
  //Get a parameter value
  public String getParameter(String key, String def) {
    return isStandalone ? System.getProperty(key, def) :
      (getParameter(key) != null ? getParameter(key) : def);
  }

  //Construct the applet
  public F3DApplet() {
  }
  //Initialize the applet
  public void init() {
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
  //Component initialization
  private void jbInit() throws Exception {
    bStart.setToolTipText("Start the application in a separate window");
    bStart.setText("Start the application");
    bStart.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bStart_actionPerformed(e);
      }
    });
    this.getContentPane().add(jPanel1, BorderLayout.CENTER);
    jPanel1.add(bStart, null);
  }
  //Start the applet
  public void start() {
  }
  //Stop the applet
  public void stop() {
  }
  //Destroy the applet
  public void destroy() {
  }
  //Get Applet information
  public String getAppletInfo() {
    return "Applet Information";
  }
  //Get parameter info
  public String[][] getParameterInfo() {
    return null;
  }
  //Main method
  public static void main(String[] args) {
    F3DApplet applet = new F3DApplet();
    applet.isStandalone = true;
    Frame frame;
    frame = new Frame() {
      protected void processWindowEvent(WindowEvent e) {
        super.processWindowEvent(e);
        if (e.getID() == WindowEvent.WINDOW_CLOSING) {
          System.exit(0);
        }
      }
      public synchronized void setTitle(String title) {
        super.setTitle(title);
        enableEvents(AWTEvent.WINDOW_EVENT_MASK);
      }
    };
    frame.setTitle("Applet Frame");
    frame.add(applet, BorderLayout.CENTER);
    applet.init();
    applet.start();
    frame.setSize(400,320);
    Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
    frame.setLocation((d.width - frame.getSize().width) / 2, (d.height - frame.getSize().height) / 2);
    frame.setVisible(true);
  }

  void bStart_actionPerformed(ActionEvent e) {
    new Growth3D().bSeparate_actionPerformed(null);
  }
}

GraphImage.java
--------------------------------------------------------------

package web;

/**
 * <p>THIS CLASS IS UNFINISHED AND CURRENTLY NOT IN USE</p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

import java.awt.*;
import javax.swing.*;

/** Canvas component for the simple mathematical model where just X/Y plot is a result. */

class GraphImage extends JComponent {
// ******************************************
    public double start_Y = 0;
    public double end_X  = 6;

    public double end_Y   =  360;
    public double initial_Y =  -360;
    public double max_Y_Change = 1;
// ******************************************
    // First curve
    public double rT[];
    public double rY[];

    // Second curve
    public double xT[];
    public double xY[];

    public boolean fixed_zero = true; // Zero line

    public GraphImage()
     {
       setBackground(Color.white);
     };

    // Moralinis grafikos palaikymas:
    private int XMA=120;
    private double smx=1, smy=1, siay=0;
    private int    sdx=0, sdy=0, sry=XMA;
    private FontMetrics Metras;
    private int vx, vy,hx,hy;
    private int SW(String s) { return Metras.stringWidth(s);};

    public int gx(double x) { return (int)Math.round(smx*x)+sdx; };
    public int gy(double y) { return sry-(int)Math.round(smy*(y-siay))+sdy; };

    private boolean space(String s,int i)
     { return Character.isWhitespace(s.charAt(i)); };

    private String _d_buf="";
            double _d_time;
            double _d_dat;
            double _d_se;
            int    _d_I;
            int    _d_L;

    private boolean Skip_Spaces()
     { if (_d_I>=_d_L) return false;
       while (space(_d_buf,_d_I) &&
             ( _d_I<_d_L )) _d_I++;
       if (_d_I>=_d_L) return false;
       if (_d_buf.charAt(_d_I)=='(') return false;
       return true;
     };

    private boolean _nextData()
    { if (!Skip_Spaces()) return false;
       int K=_d_I;
       int D;
       String s;
       D=K;

       while ((D<_d_L) &&  Character.isWhitespace(_d_buf.charAt(D)) )
        { D++; if (D>_d_L) return false; };

       if (D>=_d_L) return false;
       if  ( _d_buf.charAt(K)=='(' ) return false;

       while (! Character.isWhitespace(_d_buf.charAt(D)) )
        { D++; if (D>_d_L) return false; };
       if  ( _d_buf.charAt(D-1)=='(' ) return false;

        s=_d_buf.substring(K,D);

        _d_time =toDouble(s);

       while ((D<_d_L) &&  Character.isWhitespace(_d_buf.charAt(D)) )
        { if (D>_d_L) return false; D++; };
       K=D;

       while (! Character.isWhitespace(_d_buf.charAt(D)) )
        {  D++; if (D>_d_L) return false;};
        s=_d_buf.substring(K,D);
       _d_dat =toDouble(s);
       while ((D<_d_L) &&  Character.isWhitespace(_d_buf.charAt(D)) )
        { D++; if (D>_d_L) return false; };
       K=D;

       while (! Character.isWhitespace(_d_buf.charAt(D)) )
        { if (D>_d_L) return false; D++; };
        s=_d_buf.substring(K,D);
       _d_se =toDouble(s);

       K=D;
       _d_I=D;
       return true;
     };

 public double sqr(double x) { return x*x; };

 public void paint(Graphics g) {
    int i,j,k,l; // Ciklams
    Metras=this.getFontMetrics(getFont());
    int sLH=Metras.getHeight();
    int LH=sLH*3/2;
    int fX = (LH/8)*2;
    if (fX<2) fX=2;

    // finding X and Y intervals:
    end_Y=initial_Y=rY[0];
    end_X=0;
    for (k=1;k<rT.length;k++)
    {
     if (rY[k]>end_Y)   end_Y  =rY[k];
     if (rY[k]<initial_Y) initial_Y=rY[k];
     if (rT[k]>end_X)   end_X=rT[k];
    };

    long a=(long)Math.ceil(initial_Y);
    long b=(long) (Math.ceil(end_Y)*1.1);

    long c=(b-a)/2;
    long ak=c>30?30:3;
    long v=(b+a)/2;
    v=(v/ak)*ak; c=(c/ak)*ak;

    c+=ak;

    a=v-c; b=v+c;

    if (fixed_zero) a=0;

    initial_Y=a;
    end_Y=b;

    /****** Drawing axes ***********/

    /* butu idomu zinoti is kur bound() zino kieno ribas rast. Faktas, zino */
    Rectangle r = getBounds();
    g.setColor(Color.black);
    vx=fX+SW("000000");
    vy=fX; hx=r.width-fX; hy=r.height-2*fX-2*sLH;
    g.drawLine(vx, vy, vx, hy);
    g.drawLine(vx, hy, hx, hy);
    int y=hy, x=vx, dx= ((hx-vx)*95)/(5*100);

    smx = 0.95*(hx-vx)/end_X; sdx=vx;
    for (k=0;k<=5;k++) // Horizontali asis
     { double vT=k*(end_X/5);
       x=gx(vT);
       g.drawLine(x,y-fX,x,y+fX);
       g.drawString(Double.toString(Math.round(vT*10.0)/10.0),x,y+LH);
     };
    g.drawString("Time",(x-vx)/2,hy+2*sLH+fX);
    x=vx;

    double rAngle = end_Y-initial_Y;
    smy = 0.95*(hy-vy)/rAngle; sdy=vy; sry=hy-fX;
    siay = initial_Y;
    double dy = Math.round(rAngle/6);
    int ddT=SW("-120 o");
    int ddL=x-SW("o ");
        for (k=0;k<=6;k++) // Vertikali asis
         { double ly=k*dy+initial_Y;
           y=gy(ly);
           g.drawLine(x-fX,y,x+fX,y);
           g.drawString(Long.toString(Math.round(ly)),x-ddT,y-fX);
         };

     /****** Drawing model ***********/
     g.setColor(Color.red.darker());
     int xx=gx(rT[0]), yy=gy(rY[0]);
     for (k=1;k<rT.length;k++)
      { g.drawLine(xx,yy,xx=gx(rT[k]),yy=gy(rY[k])); };

     /****** Drawing (if present) the second curve. *****/
     if (xT!=null)
      {
         g.setColor(new Color(0,80,0));
         xx=gx(xT[0]); yy=gy(xY[0]);
         for (k=1;k<xT.length;k++)
          { g.drawLine(xx,yy,xx=gx(xT[k]),yy=gy(xY[k])); };
      }

      };

 public double toDouble(String s)
  { try {
    return Double.parseDouble(s.trim());
    } catch (Exception exc)
     { exc.printStackTrace(); };
    throw new Error ("Unable to convert '"+s+"' to double.");
  };

 public void print(String x)
  {
    System.out.println(x);
  }
}

Growth3D.java
--------------------------------------------------------------

package web;

import java.applet.*;
import java.io.*;
import java.security.*;
import java.text.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import fungimedia_3d.*;
import fungimedia_3d.Supercomputing.*;
import fungimedia_3d.io.*;
import javax.swing.event.*;

import fungimedia_3d.Measure.*;

/**
 * The main front panel of the program.
 */

public class Growth3D extends JPanel {

  static double SBI_SCALE=0.0001;
  static double S_GALVANOTROPISM = 0.01;

  BorderLayout borderLayout1 = new BorderLayout();
  BorderLayout borderLayout10 = new BorderLayout();
  BorderLayout borderLayout2 = new BorderLayout();
  BorderLayout borderLayout3 = new BorderLayout();
  BorderLayout borderLayout4 = new BorderLayout();
  BorderLayout borderLayout5 = new BorderLayout();
  BorderLayout borderLayout6 = new BorderLayout();
  BorderLayout borderLayout8 = new BorderLayout();
  JButton bParameters = new JButton();
  JButton bReturnToModel = new JButton();
  JButton bSaveImage = new JButton();
  JButton bStart = new JButton();
  JButton slX = new JButton();
  JButton slY = new JButton();
  JButton slZ = new JButton();

  CardLayout cards = new CardLayout();

  CardLayout varStatusLayout = new CardLayout();
  JCheckBox cbPause = new JCheckBox();
  CheckboxGroup currentParameterSet = new CheckboxGroup();
  GridBagLayout gridBagLayout1 = new GridBagLayout();
  GridBagLayout laBelow = new GridBagLayout();
  JLabel label6 = new JLabel();
  JLabel label8 = new JLabel();
  JLabel lbSlicePlane = new JLabel();
  JLabel lbState = new JLabel();
  JLabel lbStrategy = new JLabel();
  JLabel lbScale = new JLabel();
  JPanel model = new JPanel();
  JPanel panel1 = new JPanel();
  JPanel panel2 = new JPanel();
  JPanel panel3 = new JPanel();
  JPanel parameters = new JPanel();
  JPanel pnControls = new JPanel();
  JPanel pnSlice = new JPanel();
  JPanel pnSliceOptions = new JPanel();
  JPanel pnSc = new JPanel();
  JPanel pnSc1 = new JPanel();
  JPanel pnStatus = new JPanel();
  JPanel varStatus = new JPanel();

  JScrollBar sbRotateAround_Z = new JScrollBar();
  JScrollBar sbRotateAround_X = new JScrollBar();

  JScrollBar sbH = new JScrollBar();
  JScrollBar sbH1 = new JScrollBar();
  JScrollBar sbSlicePlane = new JScrollBar();
  JScrollBar sbThickness = new JScrollBar();
  JScrollBar sbV = new JScrollBar();
  JScrollBar sbV1 = new JScrollBar();
  JScrollBar sbScale = new JScrollBar();

  public View mainView = new View();
  public sliceView dView = new sliceView(mainView);

  public boolean isStandalone = true;
  String selectedCard = "model";

  /** Built-in option sets. */
  public final MOptions [] os = new MOptions[8];

  //Initialize the applet
  public void init() {
    try {

      os[1] = MOptions.Options.Clone(); // boletus

      MOptions.Options.Tropisms.Autotropism=true; // amanita
      os[2] = MOptions.Options.Clone();

      // Tricholoma parameter set is different from the lold model 1,2
      // vestions. The shape is far better if the complete
      // evaluation is assumed.
      MOptions.Options.Tropisms.fieldHypothesis=true; // tricholoma
      MOptions.Options.Mycelia.branchingDensity=0.06;

      os[3] = MOptions.Options.Clone();

      MOptions.Options.Gravitropism.suppose=true;
      MOptions.Options.Field.completeEvaluation=true;

      os[5] = MOptions.Options.Clone();
      MOptions.Options.Gravitropism.suppose=false;
      MOptions.Options.Vizualization.scale = 2;
      os[7] = MOptions.Options.Clone(); // spheric standard

      MOptions.Options.Autotropism_c.suppose = true;
      MOptions.Options.Autotropism_d.suppose = true;
      os[6] = MOptions.Options.Clone(); // cords

      os[5].CopyTo(MOptions.Options);

      MOptions.Options.FSensorTurn.suppose=false;
      MOptions.Options.Field.completeEvaluation=false;

      MOptions.Options.Tropisms.fieldHypothesis=false;
      MOptions.Options.Tropisms.Autotropism = false;

      MOptions.Options.Mycelia.densityDependend=false;
      MOptions.Options.Mycelia.cbBranch = false;
      MOptions.Options.Mycelia.cbGrowth = false;
      MOptions.Options.Gravitropism.suppose=false;
      MOptions.Options.Mycelia.randomComponent = 0.025;

      os[4] = MOptions.Options.Clone();

      MOptions.Options = os[2].Clone();

      jbInit();

      for (int i = 1; i < MOptions.R.length; i++) {
        MOptions.R[i]=MOptions.Options.Clone();
      }
      MOptions.R[0] = MOptions.Options;

      for (int i = 0; i < tpParaSets.getComponentCount(); i++) {
        try {
          ( (ParameterPanel) tpParaSets.getComponentAt(i)).takeFromOptions(MOptions.
              R[i]);
        }
        catch (ClassCastException ex) {
        }
      }

      if (pn_X_controls.isVisible())
      {
        adjust_X_scrollbars();
      }
    }
    catch(Exception e) {
      e.printStackTrace();
    }
    //startAutoRotation();
  }

  private void adjust_X_scrollbars()
  {
    MOptions o = MOptions.Options;
    sb_G_angle.setValue(o.Gravitropism.angle);
    sb_a2_impact.setValue((int) (o.Autotropism_i2.impact/SBI_SCALE));
    sb_gt_par.setValue((int) (o.Autotropism_c.impact/S_GALVANOTROPISM));
    sb_gt_pn.setValue((int) (o.Autotropism_d.impact/S_GALVANOTROPISM));

    cb_a2_impact.setSelected(o.Autotropism_i2.suppose);
    cb_gt_par.setSelected(o.Autotropism_c.suppose);
    cb_gt_pn.setSelected(o.Autotropism_d.suppose);
    lb_g_angle.setSelected(o.Gravitropism.suppose);

    sb_a2_adj(null);
    sb_G_angle_adj(null);
    sb_gt_par_adj(null);
    sb_gt_pn_adj(null);
  }

  //Component initialization
  private void jbInit() throws Exception {
    this.setLayout(cards);
    mainView.setAlpha(0);
    mainView.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseReleased(MouseEvent e) {
        view_mouseReleased(e);
      }
    });
    sbV.setMaximum(1000);
    sbV.setMinimum(-1000);
    sbV.setVisibleAmount(100);
    sbV.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sbV_adjustmentValueChanged(e);
      }
    });
    pnControls.setLayout(gridBagLayout1);
    sbRotateAround_Z.setMaximum(190);
    sbRotateAround_Z.setMinimum(-180);
    sbRotateAround_Z.setOrientation(0);
    sbRotateAround_Z.setToolTipText("Adjust view angle");
    sbRotateAround_Z.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sbRotateAround_Z_adjustmentValueChanged(e);
      }
    });
    pnSc.setLayout(borderLayout2);
    cbPause.setToolTipText("Temporary suspend the simulation");
    cbPause.setText("Pause");
    cbPause.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        cbPause_itemStateChanged(e);
      }
    });
    sbH.setMaximum(1000);
    sbH.setMinimum(-1000);
    sbH.setOrientation(0);
    sbH.setValue(-50);
    sbH.setVisibleAmount(100);
    sbH.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sbH_adjustmentValueChanged(e);
      }
    });
    bStart.setToolTipText("Restart the simulation from the single hyphal tip");
    bStart.setText("Start");
    bStart.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bStart_actionPerformed(e);
      }
    });
    model.setLayout(borderLayout3);
    bParameters.setToolTipText("View model parameters");
    bParameters.setText("details...");
    bParameters.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bStrategies(e);
      }
    });

    pnStatus.setLayout(laBelow);
    mainView.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
      public void mouseDragged(MouseEvent e) {
        view_mouseDragged(e);
      }
    });
    mainView.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mousePressed(MouseEvent e) {
        view_mousePressed(e);
      }
    });
    lbScale.setText("Scale:");
    sbScale.setMaximum(1000);
    sbScale.setOrientation(0);
    sbScale.setValue(200);
    sbScale.setToolTipText("Adjust magnification");
    sbScale.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sbScale_adjustmentValueChanged(e);
      }
    });
    pnSlice.setLayout(borderLayout6);
    pnSliceOptions.setLayout(gridBagLayout2);
    bReturnToModel.setText("Compute");
    dView.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mousePressed(MouseEvent e) {
        view_mousePressed(e);
      }
    });
    pnSc1.setLayout(borderLayout8);
    bReturnToModel.setText("Return");
    bReturnToModel.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bReturnToModel_actionPerformed(e);
      }
    });
    bSaveImage.setText("Save image");
    bSaveImage.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bSaveImage_actionPerformed(e);
      }
    });
    dView.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
      public void mouseDragged(MouseEvent e) {
        dView_mouseDragged(e);
      }
    });
    sbThickness.setMaximum(400);
    sbThickness.setMinimum(1);
    sbThickness.setOrientation(0);
    sbThickness.setValue(10);
    sbThickness.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sbThickness_adjustmentValueChanged(e);
      }
    });
    label6.setText("Slice thickness: ");
    sbSlicePlane.setMaximum(800);
    sbSlicePlane.setMinimum(-800);
    sbSlicePlane.setOrientation(0);
    sbSlicePlane.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sbSlicePlane_adjustmentValueChanged(e);
      }
    });
    label8.setText("Slice plane: ");

    lbState.setVisible(false);
    varStatus.setLayout(varStatusLayout);
    lbStrategy.setToolTipText("Choose a pre-programmed parameter set");
    lbStrategy.setText("Current strategy:");

    slX.setMargin(new Insets(0, 0, 0, 0));
    slX.setText("X");
    slX.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        slX_actionPerformed(e);
      }
    });
    slY.setMargin(new Insets(0, 0, 0, 0));
    slY.setText("Y");
    slY.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        slY_actionPerformed(e);
      }
    });
    slZ.setMargin(new Insets(0, 0, 0, 0));
    slZ.setText("Z");
    slZ.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        slZ_actionPerformed(e);
      }
    });
    lbSlicePlane.setHorizontalAlignment(SwingConstants.CENTER);
    lbSlicePlane.setText(" across ");
    sbH1.setOrientation(0);
    sbH1.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sbH1_adjustmentValueChanged(e);
      }
    });
    sbV1.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sbV1_adjustmentValueChanged(e);
      }
    });
    bSubstrate.setToolTipText("Edit substrates");
    bSubstrate.setText("Substrates");
    bSubstrate.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bSubstrate_actionPerformed(e);
      }
    });

    sbRotateAround_X.setMaximum(190);
    sbRotateAround_X.setMinimum(-180);
    sbRotateAround_X.setOrientation(0);
    sbRotateAround_X.setToolTipText("Adjust view angle");
    sbRotateAround_X.setBlockIncrement(10);
    sbRotateAround_X.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sbRotateAround_X_adjustmentValueChanged(e);
      }
    });

    lTurnZ.setText(" Turn Z: ");

    lTurnX.setText(" Turn X: ");

    parameters.setLayout(borderLayout9);
    bLoadParameters.setText("Load parameters...");
    bLoadParameters.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bLoadParameters_actionPerformed(e);
      }
    });
    bSaveParameters.setText("Save parameters...");
    bSaveParameters.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bSaveParameters_actionPerformed(e);
      }
    });
    bReturn.setText("Return to simulation");
    bReturn.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bReturn_actionPerformed(e);
      }
    });
    lb_g_angle.setText("Angle of gravitropic reaction: ");
    lb_g_angle.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        lb_g_angle_actionPerformed(e);
      }
    });
    cb_a2_impact.setText("Long distance autotropism b:");
    cb_a2_impact.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        lb_a2_impact_actionPerformed(e);
      }
    });
    sb_G_angle.setMaximum(190);
    sb_G_angle.setOrientation(0);
    sb_G_angle.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sb_G_angle_adj(e);
      }
    });
    sb_a2_impact.setMaximum(100);
    sb_a2_impact.setMinimum(-100);
    sb_a2_impact.setOrientation(0);
    sb_a2_impact.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sb_a2_adj(e);
      }
    });
    pn_X_controls.setLayout(gridBagLayout3);
    cb_X_Robot.setText("Robot");
    cb_X_Robot.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        cb_X_Robot_itemStateChanged(e);
      }
    });
    sb_G_angle_90.setMargin(new Insets(0, 0, 0, 0));
    sb_G_angle_90.setText("90");
    sb_G_angle_90.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        sb_G_angle_90_actionPerformed(e);
      }
    });

    sb_gt_par.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sb_gt_par_adj(e);
      }
    });
    sb_gt_par.setOrientation(0);
    sb_gt_par.setMinimum(-100);
    sb_gt_par.setMaximum(100);
    cb_gt_par.setText("Parallel galvanotropism");
    cb_gt_par.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        lb_gt_par_actionPerformed(e);
      }
    });

    bv0.setToolTipText("Front view");
    bv0.setMargin(new Insets(0, 0, 0, 0));
    bv0.setText("0");
    bv0.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bv0_actionPerformed(e);
      }
    });
    bv1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bv1_actionPerformed(e);
      }
    });
    bv1.setText("R");
    bv1.setToolTipText("Repaint");
    bv1.setMargin(new Insets(0, 0, 0, 0));

    tpParaSets.setTabPlacement(JTabbedPane.RIGHT);
    tpParaSets.addChangeListener(new javax.swing.event.ChangeListener() {
      public void stateChanged(ChangeEvent e) {
        tpParaSets_stateChanged(e);
      }
    });
    bApplyToAll.setText("Make this page identical");
    bApplyToAll.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bApplyToAll_actionPerformed(e);
      }
    });
    panel4.setLayout(gridBagLayout4);
    bAllIdentical.setText("All identical");
    bAllIdentical.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bAllIdentical_actionPerformed(e);
      }
    });

    pn_X_controls.setVisible(false);
    cb_gt_pn.setText("+/- galvanotropism");
    cb_gt_pn.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        lb_gt_pn_actionPerformed(e);
      }
    });
    sb_gt_pn.setMaximum(100);
    sb_gt_pn.setMinimum(-100);
    sb_gt_pn.setOrientation(0);
    sb_gt_pn.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        sb_gt_pn_adj(e);
      }
    });
    b_pmg0.setMargin(new Insets(0, 0, 0, 0));
    b_pmg0.setText("0");
    b_pmg0.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        b_pmg0_actionPerformed(e);
      }
    });
    pa2_0.setMargin(new Insets(0, 0, 0, 0));
    pa2_0.setText("0");
    pa2_0.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        pa2_0_actionPerformed(e);
      }
    });
    bplg_0.setMargin(new Insets(0, 0, 0, 0));
    bplg_0.setText("0");
    bplg_0.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bplg_0_actionPerformed(e);
      }
    });
    spSummary.addComponentListener(new java.awt.event.ComponentAdapter() {
      public void componentShown(ComponentEvent e) {
        spSummary_componentShown(e);
      }
    });
    mgFile.setText("File");
    mnSaveMyc.setToolTipText("Save mycelia.xml file, containing mycelia and all parameters");
    mnSaveMyc.setText("Save mycelia");
    mnSaveMyc.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bMcSave_actionPerformed(e);
      }
    });
    mnSaveIm.setToolTipText("Save .jpg image of this mycelia");
    mnSaveIm.setText("Save image");
    mnSaveIm.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bCopy_actionPerformed(e);
      }
    });
    mnReadMyc.setToolTipText("Open mycelia.xml file, containing mycelia and parameters");
    mnReadMyc.setText("Open mycelia file");
    mnReadMyc.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        OpenNewMycelia();
      }
    });
    mgAnalyse.setText("Analyse");
    mnSlice.setToolTipText("View slice of mycelia");
    mnSlice.setText("View slice");
    mnSlice.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bSlice_actionPerformed(e);
      }
    });
    borderLayout8.setVgap(4);
    panel1.setLayout(gridBagLayout5);
    mnMeasure.setToolTipText("Perform statistics measurements of mycelia");
    mnMeasure.setText("Measure mycelia");
    mnMeasure.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        mnMeasure_actionPerformed(e);
      }
    });
    mgShow.setText("Show");
    mnShowTips.setToolTipText("Show hyphal tips as coloured dots");
    mnShowTips.setText("Hyphal tips");
    mnShowTips.setState(true);
    mnShowTips.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        mnShowTips_actionPerformed(e);
      }
    });
    mnShowFields.setToolTipText("Show values of involved fields as short dashes next to the tip");
    mnShowFields.setText("Field vectors");
    mnShowFields.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        mnShowFields_actionPerformed(e);
      }
    });
    mnShowRootColors.setToolTipText("Colour mycelia depending on how far the current section is from the " +
    "initial mycelia developing point");
    mnShowRootColors.setText("Root distance based colors");
    mnShowRootColors.setState(false);
    mnShowRootColors.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        mnShowRootColors_actionPerformed(e);
      }
    });
    mnTurn0.setToolTipText("Turn mycelia view into the initial orientation");
    mnTurn0.setText("Standard front view");
    mnTurn0.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bv0_actionPerformed(e);
      }
    });
    mnRepaint.setToolTipText("Repaint mycelia");
    mnRepaint.setText("Repaint");
    mnRepaint.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bv1_actionPerformed(e);
      }
    });

    mnInitial.setToolTipText("Show the standard 3D view of mycelia");
    mnInitial.setText("Return to initial view");
    mnInitial.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        mnInitial_actionPerformed(e);
      }
    });
    mgSimulation.setToolTipText("Controls the simulation process");
    mgSimulation.setText("Model");
    mgSimulation.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bStrategies(e);
      }
    });
    mnRestart.setToolTipText("Start simulation from beginning, discarding current mycelia");
    mnRestart.setText("Restart");
    mnRestart.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bStart_actionPerformed(e);
      }
    });
    mnPause.setToolTipText("Temporary suspend (or resume) the current simulation");
    mnPause.setText("Pause");
    mnPause.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        mnPause_actionPerformed(e);
      }
    });
    mnSubstrates.setToolTipText("Manage substrates");
    mnSubstrates.setText("Substrates..");
    mnSubstrates.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bSubstrate_actionPerformed(e);
      }
    });
    mnParameters.setToolTipText("Edit model parameters");
    mnParameters.setText("Options..");
    mnNoColours.setText("No colours");
    mnNoColours.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        mnNoColours_actionPerformed(e);
      }
    });
    mnwCheckPoints.setToolTipText("Store each mycelia generation in the specified folder");
    mnwCheckPoints.setText("Write checkpoints to..");
    mnwCheckPoints.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cb_an_actionPerformed(e);
      }
    });
    jMenu1.setToolTipText("Specialised routines");
    jMenu1.setText("Special");
    jMenuItem1.setText("InfraScan");
    jMenuItem1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jMenuItem1_actionPerformed(e);
      }
    });
    jMenuItem2.setText("InfraStat");
    jMenuItem2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jMenuItem2_actionPerformed(e);
      }
    });
    choice1.setToolTipText("Choose a pre-programmed parameter set");
    mnRobot.setToolTipText("\"Robot\" records and repeats parameter manipulations in time");
    mnRobot.setText("Robot");
    mnRobot.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        robotPanelStateEvent(e);
      }
    });
    jMenuItem3.setText("Exit program");
    jMenuItem3.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jMenuItem3_actionPerformed(e);
      }
    });
    jMenu2.setText("Help");
    jMenuItem4.setText("Keys for running in standalone mode");
    jMenuItem4.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jMenuItem4_actionPerformed(e);
      }
    });
    jPanel1.setLayout(borderLayout7);
    jButton1.setText("Return to main section");
    jButton1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jButton1_actionPerformed(e);
      }
    });
    taKeys.setBackground(SystemColor.menu);
    taKeys.setContentType("text/html");
    pnSc.add(sbH, BorderLayout.SOUTH);
    pnSc.add(sbV, BorderLayout.EAST);
    pnSc.add(mainView, BorderLayout.CENTER);
    pnControls.add(lbScale,                              new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnControls.add(sbScale,                                    new GridBagConstraints(3, 0, 5, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnControls.add(bStart,               new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 20, 0));
    pnControls.add(cbPause,               new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnControls.add(sbRotateAround_Z,                   new GridBagConstraints(3, 1, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnControls.add(sbRotateAround_X,               new GridBagConstraints(5, 1, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnControls.add(lTurnZ,             new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0));
    pnControls.add(lTurnX,            new GridBagConstraints(4, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0));
    pnControls.add(bv1,        new GridBagConstraints(7, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnControls.add(bv0,      new GridBagConstraints(6, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0));
    model.add(pnSc, BorderLayout.CENTER);
    model.add(pnControls, BorderLayout.NORTH);

    model.add(pnStatus,  BorderLayout.SOUTH);
     // Status control buttons are removed in this version.

    pnStatus.add(bParameters,                                   new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 0, 5, 3), 0, 0));

    pnStatus.add(varStatus,                     new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));

    varStatus.add(panel3,   "JLabel");
    varStatus.add(lbState,  "state");

    pnStatus.add(bSubstrate,             new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnStatus.add(choice1,                 new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 4, 0, 4), 0, 0));
    pnStatus.add(pn_X_controls,           new GridBagConstraints(0, 1, 4, 1, 10.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pn_X_controls.add(lb_g_angle,                            new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pn_X_controls.add(sb_G_angle,                             new GridBagConstraints(2, 0, 1, 1, 1.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
    pn_X_controls.add(cb_a2_impact,                            new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0));
    pn_X_controls.add(sb_a2_impact,                              new GridBagConstraints(6, 0, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
    pn_X_controls.add(cb_X_Robot,                        new GridBagConstraints(8, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pn_X_controls.add(sb_G_angle_90,                      new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pn_X_controls.add(cb_gt_par,               new GridBagConstraints(3, 1, 2, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pn_X_controls.add(sb_gt_par,               new GridBagConstraints(6, 1, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
    pn_X_controls.add(cb_gt_pn,            new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pn_X_controls.add(sb_gt_pn,          new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
    pn_X_controls.add(b_pmg0,   new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pn_X_controls.add(pa2_0,  new GridBagConstraints(4, 0, 2, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pn_X_controls.add(bplg_0, new GridBagConstraints(5, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));

    pnSlice.add(pnSliceOptions, BorderLayout.NORTH);
    pnSliceOptions.add(label6,      new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnSliceOptions.add(sbThickness,       new GridBagConstraints(2, 0, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnSliceOptions.add(bSaveImage,       new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0));
    pnSliceOptions.add(bReturnToModel,       new GridBagConstraints(5, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0));

    pnSlice.add(pnSc1, BorderLayout.CENTER);
    pnSc1.add(dView, BorderLayout.CENTER);
    pnSc1.add(sbV1,  BorderLayout.EAST);
    pnSc1.add(sbH1,  BorderLayout.SOUTH);
    pnSlice.add(panel1,  BorderLayout.SOUTH);
    panel1.add(label8,  new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 10));
    panel1.add(sbSlicePlane,   new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    panel1.add(panel2,   new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    panel2.add(lbSlicePlane, null);
    panel2.add(slX, null);
    panel2.add(slY, null);
    panel2.add(slZ, null);

    this.add(model,       "model");
    this.add(parameters,  "para");
    this.add(pnSlice,   "density");
    this.add(pnSubstrate, "substrate");
    this.add(pnMeasure,  "measure");

    panel3.add(lbStrategy, null);
    parameters.add(panel4, BorderLayout.SOUTH);
    panel4.add(bLoadParameters,       new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 5, 0), 0, 0));
    panel4.add(bSaveParameters,      new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 0, 5, 0), 0, 0));
    panel4.add(bApplyToAll,      new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 0, 5, 0), 0, 0));
    panel4.add(bReturn,         new GridBagConstraints(5, 0, 1, 1, 1.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 0, 5, 0), 0, 0));
    panel4.add(bAllIdentical,    new GridBagConstraints(4, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    parameters.add(tpParaSets,  BorderLayout.CENTER);
    tpParaSets.add(pn_Para_Standard,   "Standard");
    tpParaSets.add(pn_Para_Leading,    "Leading");
    tpParaSets.add(pn_Para_Secondary,  "Secondary");
    tpParaSets.add(spSummary,  "Summary");

    sbH1.setMinimum(sbH.getMinimum());
    sbH1.setMaximum(sbH.getMaximum());

    sbV1.setMinimum(sbV.getMinimum());
    sbV1.setMaximum(sbV.getMaximum());

    choice1.addItem("<modified>");
    choice1.addItem("Boletus");
    choice1.addItem("Amanita");
    choice1.addItem("Tricholoma");
    choice1.addItem("Random");
    choice1.addItem("Conus");
    choice1.addItem("Cords");
    choice1.addItem("Standard");

    choice1.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        choice1_itemStateChanged(e);
      }
    });

    spSummary.getViewport().add(taSummary, null);
    jMenu.add(mgFile);
    jMenu.add(mgAnalyse);
    jMenu.add(mgShow);
    jMenu.add(mgSimulation);
    jMenu.add(jMenu2);

    /** @todo: Adding this menu gives access to the built-in specialised routines. */
    //jMenu.add(jMenu1);

    mgFile.add(mnSaveMyc);
    mgFile.add(mnSaveIm);
    mgFile.addSeparator();
    mgFile.add(mnReadMyc);
    mgFile.add(mnwCheckPoints);
    mgFile.addSeparator();
    mgFile.add(jMenuItem3);
    mgAnalyse.add(mnSlice);
    mgAnalyse.add(mnMeasure);
    mgAnalyse.addSeparator();
    mgAnalyse.add(mnInitial);
    mgShow.add(mnShowTips);
    mgShow.add(mnShowFields);
    mgShow.add(mnShowRootColors);
    mgShow.addSeparator();
    mgShow.add(mnNoColours);
    mgShow.addSeparator();
    mgShow.add(mnTurn0);
    mgShow.add(mnRepaint);
    mgSimulation.add(mnRestart);
    mgSimulation.add(mnPause);
    mgSimulation.addSeparator();
    mgSimulation.add(mnSubstrates);
    mgSimulation.add(mnParameters);
    mgSimulation.add(mnRobot);
    jMenu1.add(jMenuItem1);
    jMenu1.add(jMenuItem2);
    jMenu2.add(jMenuItem4);
    this.add(jPanel1,   "keys");
    jPanel1.add(jPanel2,  BorderLayout.SOUTH);
    jPanel2.add(jButton1, null);
    jPanel1.add(jScrollPane1,  BorderLayout.CENTER);
    jScrollPane1.getViewport().add(taKeys, null);

    choice1.setSelectedIndex(2); // Amanita
    //loadBuiltInOptions(choice1.getSelectedItem());

    pnSubstrate.master=this;

  }

  //Start the applet
  public void start() {
    mainView.deltaX = getWidth()/2;
    mainView.deltaY = getHeight()/2;
    sbH.setValue(0);
    sbV.setValue(0);
  }

  //Stop the applet
  public void stop() {
   cbPause.setSelected(true);
   mainView.mycelia.pause = true;
   state(mainView.mycelia.getInfo().toString());

  }
  //Destroy the applett
  public void destroy() {
    mainView.mycelia.pause=true;
  }
  //Get Applet information
  public String getAppletInfo() {
    return Main.HEADER;
  }
  //Get parameter info
  public String[][] getParameterInfo() {
    return null;
  }

  /** Command line parameters. */
  public void run_parameters(String[] args)
  {
    String arch=System.getProperty("os.arch");
    if (arch!=null)
      if (arch.equalsIgnoreCase("x86"))
      {
        System.out.println("x86 machine, default number of processors is set to 1");
        ParallelProcessor.N_processors=1;
      }
    // read paramters:
    if (args.length>0)
    {
      for (int i = 0; i < args.length; i++) {
        if (args[i].equalsIgnoreCase("-p"))
        {
            try {
              ParallelProcessor.N_processors =
                  Integer.parseInt(args[i + 1]);
              i++;
            }
            catch (NumberFormatException ex1) {
              System.out.println("Number of processors, "+args[i+1]+" is not an integer.");
            }
        }

        if (args[i].equalsIgnoreCase("-f"))
        {
          try {
            MyceliaIO.load(mainView.mycelia, args[i + 1]);
            mainView.mycelia.pause=false;
            cbPause.setSelected(false);
            i++;

          }
          catch (IOException ex) {
            ex.printStackTrace();
          }
        }
      }
    }
    System.out.println("Processors: "+ParallelProcessor.N_processors);
  }

  PrintStream autoDocStream = null;
  int lastSavedMoment = -1;
  double wg = 0;
  double wa = 0;
  double w_c = 0;
  double w_d = 0;
  synchronized void autoDoc()
  {
    if (autoDocStream!=null)
    if (lastSavedMoment!=mainView.mycelia.timer)
    {
    try {
      if (lastSavedMoment>=0)
      {
      autoDocStream.println(lastSavedMoment +
                            "\t " + wg +
                            "\t " + wa +
                            "\t " + w_c +
                            "\t " + w_d
                            );

      System.out.println("Saving autodoc "+lastSavedMoment);
      }

      lastSavedMoment = mainView.mycelia.timer;
      MOptions o = MOptions.Options;
      wg = gv(o.Gravitropism.angle, o.Gravitropism.suppose);
      wa = gv(o.Autotropism_i2.impact, o.Autotropism_i2.suppose);
      w_c = gv(o.Autotropism_c.impact, o.Autotropism_c.suppose);
      w_d = gv(o.Autotropism_d.impact, o.Autotropism_d.suppose);
      autoDocStream.flush();
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }
    }
  }

  private double gv(double x, boolean suppose)
  {
    if (suppose) return x;
            else return 0;
  }

  void bStart_actionPerformed(ActionEvent e) {

   mainView.mycelia.restart();
   pnMeasure.setMycelia(mainView.mycelia);
   cbPause.setSelected(false);
   startObservator();
   bStart.setText("Start new");
   dView.lTime = -1;
   varStatusLayout.show(varStatus, "state");
   mainView.setAlpha(0);
   mainView.setBeta(0);

   if (pn_X_controls.isVisible())
   try {
     adjust_X_scrollbars();

     System.out.println("Opening AUTODOC");
     mainView.mycelia.timer = 0;
     autoDocStream = new PrintStream(
            new BufferedOutputStream(
            new FileOutputStream(autoDocFileName)));
     autoDoc();
   }
   catch (FileNotFoundException ex) {
   }

   mainView.repaint(1000);
  }

  void cbPause_itemStateChanged(ItemEvent e) {
   boolean b = cbPause.isSelected();
   mainView.mycelia.pause = b;
   mnPause.setText(b?"Continue":"Pause");
   if (autoDocStream!=null) autoDocStream.flush();
  }

 int RotationVector = 1;

  Thread observator = null;
  void startObservator()
   {
     if (observator!=null) return; // present and running
     Thread t =
     new Thread()
      {
        public void run()
         {
           int time = -1;
           while (true)
           {
           try {
             Thread.sleep(1000);
           }
           catch (InterruptedException ex) {
           }
           Mycel.info info = mainView.mycelia.getInfo();
           if (info!=null)
           {
           if (info.time!=time)
           { System.out.println(info.toString());
             time = info.time;
             state(info.toString());
           }
           } else state("not started");
         }
         }
      };
      t.setPriority(t.MIN_PRIORITY);
      t.setDaemon(true);
      t.start();
   }

  void sbRotateAround_Z_adjustmentValueChanged(AdjustmentEvent e) {
    int angle = sbRotateAround_Z.getValue();
    mainView.setAlpha(angle);
    dView.setAlpha(angle);
    lTurnZ.setText("Z "+angle+" \u00BA");
    mainView.repaint(1000);

  }

  void sbH_adjustmentValueChanged(AdjustmentEvent e) {
    mainView.deltaX = -sbH.getValue()+getWidth()/2;;
    mainView.repaint(10);
  }

  void sbV_adjustmentValueChanged(AdjustmentEvent e) {
    mainView.deltaY = -sbV.getValue()+getHeight()/2;
    mainView.repaint(10);
  }

  void bSimulation_actionPerformed(ActionEvent e) {
   setSelectedCard("model");
  }

  void bStrategies(ActionEvent e) {
    setSelectedCard("para");
  }

 public void state(String s)
  {
    lbState.setText(s);
  }

  int m_pX = 0;
  int m_pY = 0;

  void view_mouseDragged(MouseEvent e) {
    int dirX = (e.getX()-m_pX);
    int dirY = (e.getY()-m_pY);

     m_pX = e.getX();
     int alpha = mainView.getAlpha()-dirX;
     mainView.setAlpha(alpha);
     dView.setAlpha(alpha);

     m_pY = e.getY();
     int beta = mainView.getBeta()-dirY;
     mainView.setBeta(beta);
     dView.setBeta(beta);
  }

  void view_mouseReleased(MouseEvent e) {
    int X = mainView.getBeta() % 360;
    int Z = mainView.getAlpha() % 360;
    sbRotateAround_X.setValue(X);
    sbRotateAround_Z.setValue(Z);
    lTurnX.setText("X "+X+" \u00BA");
    lTurnZ.setText("Z "+Z+" \u00BA");
  }

  void view_mousePressed(MouseEvent e) {
    m_pX = e.getX();
    m_pY = e.getY();
  }

  void sbScale_adjustmentValueChanged(AdjustmentEvent e) {
    mainView.setScale(sbScale.getValue()/200.0);
  }

  void bSeparate_actionPerformed(ActionEvent e) {
    Main.main(new String[0]);
  }

  void bCopy_actionPerformed(ActionEvent e) {
   try {
     ImageSaver.store(mainView);
    }
    catch (SecurityException sexc)
    {
      prevented();
    }
    catch (Exception ex) {
      JOptionPane.showMessageDialog
        (this, "Unable to save. Please check if you do can write to this location.");

    }
    catch (Error err)
    {
      JOptionPane.showMessageDialog
        (this, "You are running too old version of java virtual machine.\n"+
               "Image saving is supported since the version 1.4.x only.\n"+
               "Please download the more recent version at http://java.sun.com");
    }

  }

  void prevented()
   {
     JOptionPane.showMessageDialog
       (this, "Your security module prevents accessing\n"+
              "the computer file system. If you started\n"+
              "this application from a web browser,\n"+
              "please download and try standalone version instead.");
   }

  void bCopyTransSection_actionPerformed(ActionEvent e) {
   try {
     ImageSaver.store(dView);
    }
    catch (SecurityException sexc)
    {
      prevented();
    }
    catch (Exception ex) {
      JOptionPane.showMessageDialog
        (this, "Unable to save. Please check if you do can write to this location.");

    }
    catch (Error err)
    {
      JOptionPane.showMessageDialog
        (this, "You are running too old version of java virtual machine.\n"+
               "Image saving is supported since the version 1.4.x only.\n"+
               "Please download the more recent version at http://java.sun.com");
    }

  }

  public static Object primary = null;
  GridBagLayout gridBagLayout2 = new GridBagLayout();
  JButton bSubstrate = new JButton();
  SubstrateParameterPanel pnSubstrate = new SubstrateParameterPanel();

  JLabel lTurnZ = new JLabel();
  JLabel lTurnX = new JLabel();
  JComboBox choice1 = new JComboBox();
  BorderLayout borderLayout9 = new BorderLayout();
  JPanel panel4 = new JPanel();
  JButton bLoadParameters = new JButton();
  JButton bSaveParameters = new JButton();
  JButton bReturn = new JButton();
  JPanel pn_X_controls = new JPanel();
  JScrollBar sb_a2_impact = new JScrollBar();
  JScrollBar sb_G_angle = new JScrollBar();
  JCheckBox lb_g_angle = new JCheckBox();
  JCheckBox cb_a2_impact = new JCheckBox();
  GridBagLayout gridBagLayout3 = new GridBagLayout();
  JCheckBox cb_X_Robot = new JCheckBox();

  // autopause
  void bSlice_actionPerformed(ActionEvent e) {
   slX_actionPerformed(e);
   setSelectedCard("density");
  }

  void bReturnToModel_actionPerformed(ActionEvent e) {
    setSelectedCard("model");
  }

  void bSaveImage_actionPerformed(ActionEvent e) {
    bCopyTransSection_actionPerformed(e);
  }

  void dView_mouseDragged(MouseEvent e) {
    view_mouseDragged(e);
  }

  void sbThickness_adjustmentValueChanged(AdjustmentEvent e) {
    dView.H=sbThickness.getValue();
    sbSlicePlane.setVisibleAmount(dView.H);
    dView.repaint(10);
  }

  void sbSlicePlane_adjustmentValueChanged(AdjustmentEvent e) {
    mainView.plane=dView.plane=(int) (sbSlicePlane.getValue()/mainView.scale);
    dView.repaint(10);
  }

  void slX_actionPerformed(ActionEvent e) {
   dView.toggleSlicePlane(dView.PLANE_X);
   mainView.setAlpha(90);
   mainView.setBeta(0);
  }

  void slY_actionPerformed(ActionEvent e) {
   dView.toggleSlicePlane(dView.PLANE_Y);
   mainView.setAlpha(0);
   mainView.setBeta(0);
  }

  void slZ_actionPerformed(ActionEvent e) {
   dView.toggleSlicePlane(dView.PLANE_Z);
   mainView.setAlpha(90);
   mainView.setBeta(0);
  }

  void sbV1_adjustmentValueChanged(AdjustmentEvent e) {
   mainView.deltaY=dView.deltaY=-sbV1.getValue()+getHeight()/2;
   dView.repaint(100);
  }

  void sbH1_adjustmentValueChanged(AdjustmentEvent e) {
   mainView.deltaX=dView.deltaX=-sbH1.getValue()+getWidth()/2;
   dView.repaint(100);
  }

  void bSubstrate_actionPerformed(ActionEvent e) {
   pnSubstrate.setMycelia(mainView.mycelia);
   setSelectedCard("substrate");
  }

  void bMcSave_actionPerformed(ActionEvent e) {
    storeControls();
    try {
      MyceliaIO.store(mainView);
    }
    catch (SecurityException sx)
     { prevented(); }
    catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  public void OpenNewMycelia() throws Error {
    try {
      cbPause.setSelected(true);
      mainView.mycelia.pause = true;
      mnNoColours.setState(MOptions.Options.Vizualization.greyScale);
      mnShowFields.setState(MOptions.Options.Vizualization.show_field_vectors);
      mnShowTips.setState(MOptions.Options.Vizualization.show_tips);

      mnNoColours_actionPerformed(null);
      mnShowTips_actionPerformed(null);
      mnShowFields_actionPerformed(null);

      MyceliaIO.load(mainView);

      for (int i = 0; i < tpParaSets.getComponentCount(); i++) {
        try {
          ( (ParameterPanel) tpParaSets.getComponentAt(i)).takeFromOptions(MOptions.
              R[i]);
        }
        catch (ClassCastException ex1) {
        }
      }

      adjust_X_scrollbars();
      setControls();

      startObservator();
      bStart.setText("Start new");
      dView.lTime = -1;
      varStatusLayout.show(varStatus, "state");
      choice1.setSelectedIndex(0);

      pnMeasure.setMycelia(mainView.mycelia);
      pnMeasure.measureNow();

      mainView.repaint(1000);
    }
    catch (SecurityException sx)
     { prevented(); }
    catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  void sbRotateAround_X_adjustmentValueChanged(AdjustmentEvent e) {
   int angle = sbRotateAround_X.getValue();
   mainView.setBeta(angle);
   dView.setBeta(angle);
   lTurnX.setText("X "+angle+" \u00BA");
   mainView.repaint(1000);
  }

  void choice1_itemStateChanged(ItemEvent e) {
    if (choice1.getSelectedIndex()==0)
     choice1.setSelectedIndex(1);
    os[choice1.getSelectedIndex()].CopyTo(MOptions.Options);
    MOptions.Options.CopyTo(MOptions.R[0]);

    int Ntab =  pn_Para_Standard.jTabs.getComponentCount();
    for (int i = 0; i < tpParaSets.getComponentCount(); i++) {
    ParameterPanel b = null;
    try {
      b = (ParameterPanel) tpParaSets.getComponentAt(i);
      b.takeFromOptions(MOptions.Options);

    }
    catch (ClassCastException ex) {
    }
    }

    applyChanges();
  }

  void bReturn_actionPerformed(ActionEvent e) {
    applyChanges();
    setSelectedCard("model");
    choice1.setSelectedIndex(0);
  }

  void bLoadParameters_actionPerformed(ActionEvent e) {
    try {
      MOptions options = MyceliaIO.loadOptions();
      if (options != null) {
        MOptions.Options = options;
        pn_Para_Standard.takeFromOptions(options);
        choice1.setSelectedIndex(0);

        adjust_X_scrollbars();
        setControls();
      }
    }
    catch (Exception ex) {
      System.out.println("Security exception.");
    }
  }

  void bSaveParameters_actionPerformed(ActionEvent e) {
      try {
        storeControls();
        applyChanges();
        MyceliaIO.storeOptions();
      }
      catch (Exception ex) {
        System.out.println("Security exception");
      }
  }

  private void storeOptions()
  {
    for (int i = 0; i < tpParaSets.getComponentCount(); i++) {
      try {
        ( (ParameterPanel) tpParaSets.getComponentAt(i)).storeOptions(MOptions.R[i]);
      }
      catch (ClassCastException ex) {
      }
    }
  }

  private void applyChanges() {
    storeOptions();
    adjust_X_scrollbars();
   }

  void sb_G_angle_adj(AdjustmentEvent e) {
    MOptions.Options.Gravitropism.angle = sb_G_angle.getValue();
    lb_g_angle.setText("Angle "+MOptions.Options.Gravitropism.angle);
    pn_X_controls.doLayout();
    autoDoc();
  }

  void sb_a2_adj(AdjustmentEvent e) {
    NumberFormat n = NumberFormat.getInstance();
    n.setMaximumFractionDigits(4);
    double v = SBI_SCALE*sb_a2_impact.getValue();
    cb_a2_impact.setText("Autotropism b "+n.format(v));
    MOptions.Options.Autotropism_i2.impact=v;
    autoDoc();
    pn_X_controls.doLayout();
  }

  void robotPanelStateEvent(ActionEvent e) {

   boolean state = mnRobot.getState();
   pn_X_controls.setVisible(state);

   adjust_X_scrollbars();

   if (state)
   {
     varStatusLayout.show(varStatus, "state");
     fungimedia_3d.io.MyceliaIO.getSettings();
     JFileChooser chooser = new JFileChooser(fungimedia_3d.io.MyceliaIO.lastWritten);
     chooser.setApproveButtonText("Save to this file");
     chooser.setApproveButtonToolTipText("Select the log file to store your manipulations");
     chooser.setSelectedFile(new File("AutoDoc.txt"));
     chooser.setDialogTitle("Parameter alterations will now be registered in the specified file.");

     int returnVal = chooser.showOpenDialog(mainView);

     if(returnVal == JFileChooser.APPROVE_OPTION) {
      try {
        autoDocStream =
            new PrintStream(
            new BufferedOutputStream(
            new FileOutputStream(chooser.getSelectedFile())));
        autoDocFileName = chooser.getSelectedFile().getAbsolutePath();
        lbState.setText("Log file " + autoDocFileName);
      }
      catch (FileNotFoundException ex) {
        lbState.setText(ex.getMessage());
      }
     } else
        {
          pn_X_controls.setVisible(false);
          mnRobot.setState(false);
          return;
        }
    }
   Main.frame.validate();
  }

  String autoDocFileName = "AutoDoc.txt";
  JButton sb_G_angle_90 = new JButton();
  JScrollBar sb_gt_par = new JScrollBar();
  JCheckBox cb_gt_par = new JCheckBox();
  JButton bv0 = new JButton();
  JButton bv1 = new JButton();
  JTabbedPane tpParaSets = new JTabbedPane();
  ParameterPanel pn_Para_Standard = new ParameterPanel();
  ParameterPanel pn_Para_Leading = new ParameterPanel();
  ParameterPanel pn_Para_Secondary = new ParameterPanel();

  void cb_X_Robot_itemStateChanged(ItemEvent e) {
    varStatusLayout.show(varStatus, "state");

    if (cb_X_Robot.isSelected())
    {
    fungimedia_3d.io.MyceliaIO.getSettings();
    JFileChooser chooser = new JFileChooser(fungimedia_3d.io.MyceliaIO.lastOpened);
    chooser.setApproveButtonText("Repeat this log file");
    chooser.setApproveButtonToolTipText("Select the previously stored log file");
    chooser.setDialogTitle("Repeat automatically the manipulations, previously stored in the given file.");
    chooser.setSelectedFile(new File(autoDocFileName));

    int returnVal = chooser.showOpenDialog(mainView);

    if(returnVal == JFileChooser.APPROVE_OPTION) {
     try {
       File rf = chooser.getSelectedFile();
       PcRobot.setRobotFile(rf);
       lbState.setText("Robot executes " + rf.getAbsolutePath());
     }
     catch (Exception ex) {
       lbState.setText(ex.getMessage());
     }
    } else
       {
         PcRobot.setRobotFile(null);
         cb_X_Robot.setSelected(false);
         return;
       }
    } else
    {
      lbState.setText("Robot switched off");
      PcRobot.setRobotFile(null);
    }
   }

  void sb_G_angle_90_actionPerformed(ActionEvent e) {
   sb_G_angle.setValue(90);
   sb_G_angle_adj(null);
  }

  void bv0_actionPerformed(ActionEvent e) {
    sbRotateAround_X.setValue(0);
    sbRotateAround_Z.setValue(0);
    sbRotateAround_X_adjustmentValueChanged(null);
    sbRotateAround_Z_adjustmentValueChanged(null);
    setSelectedCard("model");
    mainView.repaint();
  }
  void bv1_actionPerformed(ActionEvent e) {
    mainView.repaint();
  }

  void storeControls()
  {
    MOptions.d_Vizualization v = MOptions.Options.Vizualization;
    v.scale = mainView.scale;
    v.x = sbH.getValue();
    v.y = sbV.getValue();
  }

  void setControls()
  {
    MOptions.d_Vizualization v = MOptions.Options.Vizualization;
    mnShowRootColors.setState(v.useRootDistanceColoring);
    mnShowFields.setState(v.show_field_vectors);
    mnShowTips.setState(v.show_tips);
    mainView.scale = dView.scale = v.scale;

    sbH.setValue(v.x);
    sbV.setValue(v.y);
  }

  public void fixScrollBarProblem()
  {
    sbV_adjustmentValueChanged(null);
    sbH_adjustmentValueChanged(null);
  }

  private ParameterPanel prev = null;
  JButton bApplyToAll = new JButton();
  GridBagLayout gridBagLayout4 = new GridBagLayout();
  JButton bAllIdentical = new JButton();
  JCheckBox cb_gt_pn = new JCheckBox();
  JScrollBar sb_gt_pn = new JScrollBar();
  JButton b_pmg0 = new JButton();
  JButton pa2_0 = new JButton();
  JButton bplg_0 = new JButton();
  JScrollPane spSummary = new JScrollPane();
  JTextArea taSummary = new JTextArea();

  public JMenuBar jMenu = new JMenuBar();

  public JMenu mgFile = new JMenu();
  public JMenu mgSimulation = new JMenu();
  public JMenu mgShow = new JMenu();

  JMenuItem mnSaveMyc = new JMenuItem();
  JMenuItem mnSaveIm = new JMenuItem();
  JMenuItem mnReadMyc = new JMenuItem();
  JMenu mgAnalyse = new JMenu();
  JMenuItem mnSlice = new JMenuItem();
  GridBagLayout gridBagLayout5 = new GridBagLayout();
  JMenuItem mnMeasure = new JMenuItem();
  void tpParaSets_stateChanged(ChangeEvent e) {
    Object pp = tpParaSets.getSelectedComponent();
    if (pp instanceof ParameterPanel)
    {
    ParameterPanel a = (ParameterPanel) pp;
    if (prev!=null)
     a.jTabs.setSelectedIndex(prev.jTabs.getSelectedIndex());
    prev = a;
    }
  }

  void bApplyToAll_actionPerformed(ActionEvent e) {
    ParameterPanel a = asParameterPanel(tpParaSets.getSelectedComponent());
    int tab = a.jTabs.getSelectedIndex();
    for (int i = 0; i < tpParaSets.getComponentCount(); i++) {
     ParameterPanel b = asParameterPanel(tpParaSets.getComponentAt(i));
     if (a !=b && a != null && b != null)
       {
        b.copy(a, tab);
       }
    }
  }

  void bAllIdentical_actionPerformed(ActionEvent e) {
    ParameterPanel a = asParameterPanel(tpParaSets.getSelectedComponent());
    int Ntab = a.jTabs.getComponentCount();
    for (int i = 0; i < tpParaSets.getComponentCount(); i++) {
     ParameterPanel b = asParameterPanel(tpParaSets.getComponentAt(i));
     if (a !=b && a != null && b != null)
       {
        for (int tab = 0; tab < Ntab; tab++) {
          b.copy(a, tab);
        }
       }
    }
  }
  
  ParameterPanel asParameterPanel(Component c) {
	  if (c instanceof ParameterPanel) {
		  return (ParameterPanel) c;
	  }
	  Component view = null;
	  if (c instanceof JScrollPane) {
		  JScrollPane scroll = (JScrollPane) c;
		  view = scroll.getViewport().getView();
		  if (view instanceof ParameterPanel) {
			  return (ParameterPanel) view;
		  }
	  }	  
	  return null;
  }

  void sb_gt_par_adj(AdjustmentEvent e) {
   NumberFormat n = NumberFormat.getInstance();
   n.setMaximumFractionDigits(4);
   double v = sb_gt_par.getValue()*S_GALVANOTROPISM;
   cb_gt_par.setText("|| galvanotropism "+n.format(v));
   MOptions.R[0].Autotropism_c.impact=v;
   autoDoc();
   pn_X_controls.doLayout();
 }

  void sb_gt_pn_adj(AdjustmentEvent e) {
    NumberFormat n = NumberFormat.getInstance();
    n.setMaximumFractionDigits(4);
    double v = sb_gt_pn.getValue()*S_GALVANOTROPISM;
    MOptions.R[0].Autotropism_d.impact = v;
    cb_gt_pn.setText("+/- galvanotropism "+n.format(v));
    autoDoc();
    pn_X_controls.doLayout();
  }

  void sb_a2_impact_2_actionPerformed(ActionEvent e) {
   MOptions.R[0].Autotropism_i2.suppose = cb_a2_impact.isSelected();
   autoDoc();
  }

  void lb_gt_par_actionPerformed(ActionEvent e) {
    MOptions.R[0].Autotropism_c.suppose = cb_gt_par.isSelected();
    autoDoc();
  }

  void lb_gt_pn_actionPerformed(ActionEvent e) {
    MOptions.R[0].Autotropism_d.suppose = cb_gt_par.isSelected();
    autoDoc();
  }

  void lb_g_angle_actionPerformed(ActionEvent e) {
    MOptions.R[0].Gravitropism.suppose = cb_gt_par.isSelected();
    autoDoc();
  }

  void lb_a2_impact_actionPerformed(ActionEvent e) {
    MOptions.R[0].Autotropism_i2.suppose = cb_gt_par.isSelected();
    autoDoc();
  }

  void b_pmg0_actionPerformed(ActionEvent e) {
    sb_gt_pn.setValue(0);
    sb_gt_pn_adj(null);
  }

  void bplg_0_actionPerformed(ActionEvent e) {
    sb_gt_par.setValue(0);
    sb_gt_par_adj(null);
  }

  void pa2_0_actionPerformed(ActionEvent e) {
    sb_a2_impact.setValue(0);
    sb_a2_adj(null);
  }

  void cb_an_actionPerformed(ActionEvent e) {
    try {

    if (!mnwCheckPoints.getState())
      {
        mainView.mycelia.AutoSave.autoSave = false;
        return;
      }  else
      {
        mainView.mycelia.AutoSave.autoSave = true;
        mainView.mycelia.AutoSave.autoSaveInterval = 1;

        JFileChooser chooser = new JFileChooser(fungimedia_3d.io.MyceliaIO.lastWritten);
        chooser.setApproveButtonText("Store results here");
        chooser.setApproveButtonToolTipText("Store .mycelia.xml checkpoints to the selected folder.");
        chooser.setDialogTitle("Select folder to store checkpoints.");
        chooser.setFileSelectionMode(chooser.DIRECTORIES_ONLY);
        chooser.setSelectedFile(new File(mainView.mycelia.AutoSave.autoSaveFolder));
        int returnVal = chooser.showOpenDialog(mainView);

        if(returnVal == JFileChooser.APPROVE_OPTION) {
          mainView.mycelia.AutoSave.autoSaveFolder = chooser.getSelectedFile().getAbsolutePath();
         }
         else
         {
           // cancel
           mnwCheckPoints.setState(false);
           mainView.mycelia.AutoSave.autoSave = false;
           System.out.println("Autosave cancelled.");
         }
      }
    }
    catch (SecurityException sx)
     { prevented(); }

  }

  void spSummary_componentShown(ComponentEvent e) {
    storeOptions();
    storeControls();
    taSummary.setText("Mycelia: "+mainView.mycelia.getInfo().toString()+"\r\n\r\n");

    taSummary.append(MOptions.Options.DocumentAllText());
    taSummary.setSelectionStart(0);
    taSummary.setSelectionEnd(0);
  }

  JCheckBoxMenuItem mnShowTips = new JCheckBoxMenuItem();
  JCheckBoxMenuItem mnShowFields = new JCheckBoxMenuItem();
  JCheckBoxMenuItem mnShowRootColors = new JCheckBoxMenuItem();
  JMenuItem mnTurn0 = new JMenuItem();
  JMenuItem mnRepaint = new JMenuItem();
  JMenuItem mnRestart = new JMenuItem();

  void mnMeasure_actionPerformed(ActionEvent e) {
   setSelectedCard("measure");
  }

  void mnShowTips_actionPerformed(ActionEvent e) {
    mainView.show_tips=mnShowTips.getState();
    MOptions.Options.Vizualization.show_tips=mainView.show_tips;
    repaint();
  }

  void mnShowFields_actionPerformed(ActionEvent e) {
    boolean vectors = mnShowFields.getState();
    MOptions.Options.Vizualization.show_field_vectors=vectors;
    repaint();
  }

  void mnShowRootColors_actionPerformed(ActionEvent e) {
    boolean rc = mnShowRootColors.getState();
    MOptions.R[0].Vizualization.useRootDistanceColoring=
    MOptions.Options.Vizualization.useRootDistanceColoring=
    rc;
    repaint();
  }

  public void replaceBottom(Component newBottom)
  {
    pnStatus.removeAll();
    pnStatus.setLayout(new GridLayout());
    pnStatus.add(newBottom);
  }

  public boolean isRunable() {
    return runable;
  }
  public void setRunable(boolean runable) {
    this.runable = runable;
    bStart.setVisible(runable);
    cbPause.setVisible(runable);

  }

  public JMenu getMgAnalyse() {
    return mgAnalyse;
  }
  public JMenu getMgFile() {
    return mgFile;
  }
  public JMenu getMgShow() {
    return mgShow;
  }

  boolean runable = true;
  public MPanel pnMeasure = new MPanel(this);
  JMenuItem mnInitial = new JMenuItem();
  JMenuItem mnPause = new JMenuItem();
  JMenuItem mnSubstrates = new JMenuItem();
  JMenuItem mnParameters = new JMenuItem();
  JCheckBoxMenuItem mnNoColours = new JCheckBoxMenuItem();
  JCheckBoxMenuItem mnwCheckPoints = new JCheckBoxMenuItem();
  JMenu jMenu1 = new JMenu();
  JMenuItem jMenuItem1 = new JMenuItem();
  JMenuItem jMenuItem2 = new JMenuItem();
  JCheckBoxMenuItem mnRobot = new JCheckBoxMenuItem();
  JMenuItem jMenuItem3 = new JMenuItem();
  JMenu jMenu2 = new JMenu();
  JMenuItem jMenuItem4 = new JMenuItem();
  JPanel jPanel1 = new JPanel();
  BorderLayout borderLayout7 = new BorderLayout();
  JPanel jPanel2 = new JPanel();
  JButton jButton1 = new JButton();
  JScrollPane jScrollPane1 = new JScrollPane();
  JEditorPane taKeys = new JEditorPane();

  void mnInitial_actionPerformed(ActionEvent e) {
   setSelectedCard("model");
  }
  public String getSelectedCard() {
    return selectedCard;
  }
  public void setSelectedCard(String _selectedCard) {
    this.selectedCard = _selectedCard;
    cards.show(this, _selectedCard);
  }

  void mnPause_actionPerformed(ActionEvent e) {
    boolean b = !cbPause.isSelected();
    cbPause.setSelected(b);
    cbPause_itemStateChanged(null);
  }

  void mnNoColours_actionPerformed(ActionEvent e) {
    boolean c = mnNoColours.getState();
    mnNoColours.setState(c);
    MOptions.Options.Vizualization.greyScale = c;
    for (int i = 0; i < MOptions.R.length; i++) {
      MOptions.R[i].Vizualization.greyScale = c;
    }
  }

  public void infraScan()
  {
    try {
      mainView.mycelia.AutoSave.autoSave = false;
      MOptions.Options.HTropism.suppose = true;
      MOptions.Options.HTropism.layer = 4;
      int interval = 220;
      double r = 1.599999999999999E-4 ; //0;
      PrintStream p = new PrintStream(new FileOutputStream("scan"+interval+".txt"));
      p.println("r \t length \ttips\t unit\t bPoint");
      while (r < 0.004) {
        System.out.println("Scanning r = "+r);
        MOptions.Options.HTropism.impact = r;
        bStart_actionPerformed(null);

        if (r == 0) {
          r = 1E-5;
        }
        else {
          r = r * Math.sqrt(Math.sqrt(2));

        }
        while (mainView.mycelia.timer < interval) {
          //System.out.println("Waiting "+mainView.mycelia.timer);
          try {
            Thread.sleep(10);
          }
          catch (InterruptedException ex) {
          }
        }

        mainView.mycelia.pause = true;
        System.out.println("OK "+mainView.mycelia.timer);
        Mycel.info info = mainView.mycelia.getInfo();
        double hgu = info.length / info.tips;

        p.println(r+" \t"+info.length + " \t" + info.tips + "\t " + hgu + "\t " +
                  info.bPoints);
        p.flush();

        MyceliaIO.store(mainView.mycelia, "htr_"+r+".mycelia.xml");

      }
      p.close();
    }
    catch (Exception ex1) {
      ex1.printStackTrace();
      System.out.println("Infrascan finished with problems");
      return;
    }
    System.out.println("Infrascan complete!");
  }

  void jMenuItem1_actionPerformed(ActionEvent e) {
    System.out.println("Starting infrascan");
    infraScan();
  }

  /** Serial analysis of the colony statistics. */
  public void infraStat()
  {
    String root = "D:\\Flat\\";
    String file = "\\__460.mycelia.xml";
    String Case[] = new String []
        {
         "C0","C0002","C002","C00003","C0005","C0007"
        };
    String subdir = "infraStat\\";
    new File(subdir).mkdirs();

    for (int iu = 0; iu < Case.length; iu++) {
      String f = root+Case[iu]+file;
      try {
        MyceliaIO.load(mainView.mycelia, f);
        pnMeasure.setMycelia(mainView.mycelia);
        pnMeasure.measureNow();
        pnMeasure.jStatistics.dropMeasurements(subdir+Case[iu]+".prn");
        System.out.println("Finished "+f);
        mainView.repaint(1000);
      }
      catch (Exception ex) {
        ex.printStackTrace();
      }
    }
    System.out.println("InfraStat is finished");

  }

  void jMenuItem2_actionPerformed(ActionEvent e) {
    infraStat();
  }

  void jMenuItem3_actionPerformed(ActionEvent e) {
    System.exit(0);
  }

  void jMenuItem4_actionPerformed(ActionEvent e) {
    taKeys.setText(

        "<p>FungiMedia_3D.jar is a cross platform java "+
        "application, able to run on any computers, supporting java 1.4 or "+
        "compatible. Depending from the platform and from your purpose, "+
        "you may need to run this file from a command line. On the most of "+

        "supercomputers, this is the only way to run the program.</p> "+

        "<p>The program itself is caled by the following command:</p> "+

        "<p><b>java -jar FungiMedia_3D.jar -f "+
        "./taskFile.mycelia.xml -c </b><i>other keys</i></p> "+

        "<p>The following keys are supported</p> "+

        "<p>-f <i>file</i> "+
        "specifies the task file that is usually prepared running program "+
        "under Windows of other graphic environment. </p> "+

        "<p>-c Blocks the "+
        "programs graphic user interface. The key is mandatory in a bath "+
        "mode where the interacion with user is not possible. It can also "+
        "be used to run prepared task under Windows, resulting faster "+
        "execution.</p> "+

        "<p>-save <i>interval</i> "+
        "Specifies the interval, in time units, for saving the current "+
        "situation in a standard task file. -save 1 is used to create "+
        "sets that can be later converted into animations. If this is not "+
        "planned, better to use larger values, as saving takes execution "+
        "time. The default value is 10.</p> "+

        "<p>-until&nbsp;<i>time</i> "+
        "Specifies the time (in time units, not the absolute time) to "+
        "terminate the program. The key is used when we know how far we "+
        "need to continue.</p> "+

        "<p>-r <i>file</i> "+
        "Specifies (if required) the robot file, containing changes of "+
        "adjustable parameters.</p> "+

        "<p>-output <i>folder</i> "+
        "Specifies the folder where results must be written. By default, "+
        "results are written to the folder from where the program was "+
        "started.</p> "+

        "<p>-p <i>number_of_processors</i> "+
        "Specifies the expected number of processors on a multiprocessor "+
        "computers. It must be equal or a little larger than the actual "+
        "number of processors used. Too large values slow the execution. "+
        "The program cannot use more processors than it was specified with "+
        "this key.</p> "
    );
   setSelectedCard("keys");
  }

  void jButton1_actionPerformed(ActionEvent e) {
   setSelectedCard("model");
  }

}

NumericField.java
--------------------------------------------------------------

package web;
import javax.swing.*;
/**
 * <p> </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version not attributable
 * @license General public license (GPL)
 */

public class NumericField extends JTextField {
    public NumericField() {
     }
    void set(double value) { setText(""+value); };
    double get()
    {
      try {
        return Double.parseDouble(getText().trim());
      }
      catch (NumberFormatException ex) {
       JOptionPane.showMessageDialog(null, "Invalid input value "+getText());
       return 0;
      }
    }
  }

ParameterPanel.java
--------------------------------------------------------------

package web;

import java.awt.*;
import java.awt.event.*;
import fungimedia_3d.MOptions;
import javax.swing.*;
import javax.swing.event.*;
import java.io.*;

/**
 * <p> </p>
 * <p>Sight 2.10 System of Internet robots for silicon cloning</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Ulm university</p>
 * @version 2.1.0
 *
 */

public class ParameterPanel extends JPanel {

  public static boolean FULL_VERSION = true;

  public ParameterPanel() {
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
  private void jbInit() throws Exception {

    cbGrow.setText("Grow only when there is less than");
    label2.setText("neighbouring tips.");
    pGrow.setColumns(4);
    label3.setText("The neighbouring tips are the tips in radius");
    pR_crit.setColumns(4);
    pOld_grow.setColumns(4);
    label4.setText("time units");
    pLong.setColumns(4);
    cbLong.setText("Stop growing after the section length exceeds ");
    label5.setText("length units");
    this.setLayout(gridBagLayout1);
    label7.setText("Growth rate (distance units per time unit) ");
    dGrowth.setColumns(7);
    label9.setText("length units");
    label11.setText("Hyphae density field is generated by:");
    cbOld_branch.setText("Stop branching after the tip age exceeds");
    label12.setText("time units");

    cbAutotropism.setText("Suppose negative autotropism");
    cbAutotropism.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        cbAutotropism_itemStateChanged(e);
      }
    });
    taInertia.setEnabled(false);
    taInertia.setText("");
    lbInertia.setEnabled(false);
    lbInertia.setText("Persistence factor (0..1)");
    cb_FSensorTurn.setText("Turn primary sensor by");
    cb_FSensorTurn.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        cbFSensorTurn_itemStateChanged(e);
      }
    });
    lbAngleExplain.setEnabled(false);
    lbAngleExplain.setText("degres around the hyphal axis");
    FSensorTurn_Angle.setEnabled(false);
    cb_HDF_tips.setText("tips");
    cb_HDF_tips.setSelected(true);
    cb_HDF_bpoints.setText("branch points");
    cb_HDF_bpoints.setMnemonic('0');
    cb_HDF_bpoints.setSelected(true);
    cb_HDF_all.setText("all mycelia (slow!)");
    cb_HDF_all.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        cb_HDF_all_itemStateChanged(e);
      }
    });
    lbChargeUnit.setEnabled(false);
    lbChargeUnit.setText("Charge unit length");
    taChargeUnit.setEnabled(false);
    lb_g_explain.setEnabled(false);
    lb_g_explain.setText("sensitivity (0..1)");
    cb_Gravitorpism.setText("Assume gravitropism by angle");
    cb_Gravitorpism.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        ch_Gravitorpism_itemStateChanged(e);
      }
    });
    ch_g_angle.setEnabled(false);
    ch_g_angle.setText("");
    ch_g_impact.setEnabled(false);
    ch_g_impact.setText("");
    cb_g_Turn.setEnabled(false);
    cb_g_Turn.setText("Turn gravitropic sensor by");
    cb_g_Turn.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        cb_g_Turn_itemStateChanged(e);
      }
    });
    ch_g_Turn.setEnabled(false);
    ch_g_Turn.setText("");
    lb_g_turn_explain.setEnabled(false);
    lb_g_turn_explain.setText("degres around the hyphal axis");
    cb_beta_turn.setText("Root distance dependent gravitropic angle turn");
    cb_beta_turn.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        cb_beta_turn_itemStateChanged(e);
      }
    });
    l_rddg1.setText("Start at");
    l_rddg2.setText("end at");
    l_rddg3.setText("till value");

    cb_beta_turn_itemStateChanged(null) ;
    cb_vpl.setText("is proportional to length, coefficient");
    cb_vpl.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        cb_vpl_itemStateChanged(e);
      }
    });
    lb_vpl1.setEnabled(false);
    lb_vpl1.setText("max");
    ch_vplK.setEnabled(false);
    ch_vplK.setText("");
    ch_vplMax.setEnabled(false);
    cb_Autotropism_2.setText("Secondary long-range autotropism, impact:");
    cb_Autotropism_2.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        cb_Autotropism_2_itemStateChanged(e);
      }
    });
    ch_beta_turn_startAt.setText("");
    ch_beta_turn_endAt.setText("");
    ch_beta_turn_tillValue.setText("");

    cb_DirProportional.setText("~ sqrt(R)");
    cb_DirProportional.setSelected(true);

    cb_Inv_proportional.setText("~ 1/sqrt(R)");
    pnGrBr.setLayout(gridBagLayout2);
    pnLimits.setLayout(gridBagLayout5);
    cbOld_grow.setText("Stop growing after the tip age exceeds ");
    pnTropisms.setLayout(gridBagLayout6);
    pnNeighbourhood.setLayout(gridBagLayout7);
    cbDensityField.setText("Density field regulates branching");
    cbDensityField.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cbDensityField_actionPerformed(e);
      }
    });
    cbDensityField.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        cbDensityField_itemStateChanged(e);
      }
    });
    cbRandomBranching.setText("If the tip can branch, the probability that it will branch during " +
    "the time unit is ");
    cbRandomBranching.setSelected(true);
    cbRandomBranching.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        cbRandomBranching_itemStateChanged(e);
      }
    });
    pBrProb.setColumns(3);
    cbBranch.setText("Branch only when there is less than");
    cbBranch.setSelected(true);
    pBranch.setColumns(4);
    label8.setText("%");
    label1.setText("neighbouring tips.");
    cb_Optimal_IBO.setText("Optimal initial branch orientation");
    jLabel1.setFont(new java.awt.Font("Dialog", 1, 11));
    jLabel1.setText("Branching");
    jLabel2.setText("Growth");
    jLabel2.setFont(new java.awt.Font("Dialog", 1, 11));
    jLabel3.setFont(new java.awt.Font("Dialog", 1, 11));
    jLabel3.setText("Autotropisms");
    jLabel4.setFont(new java.awt.Font("Dialog", 1, 11));
    jLabel4.setText("Gravitropism");
    cb_Autotropism_3.setText("Tertiary long range autotropism, impact");
    ch_Autotropism_3.setText("");
    cb_FSensorTurn_2.setActionCommand("Turn this autotropic sensor by");
    cb_FSensorTurn_2.setText("Turn secondary sensor by");
    FSensorTurn_Angle_2.setText("");
    cb_FSensorTurn_3.setText("Turn tertiary sensor by");
    FSensorTurn_Angle_3.setSelectionStart(11);
    FSensorTurn_Angle_3.setText("");
    lb_BrAngleMax.setText("Maximal branch angle");
    jLabel5.setFont(new java.awt.Font("Dialog", 1, 11));
    jLabel5.setText("Special tropisms");
    cb_HPTropism.setText("Horizontal plane tropism, impact");
    cb_HPTropism.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cb_HPTropism_actionPerformed(e);
      }
    });
    pHP_tropism.setText("");
    pnDestructor.setLayout(gridBagLayout3);
    cb_Die_agmin.setText("Minimum age");
    p_Die_amn.setText("");
    cb_Die_agmax.setText("Maximum age");
    p_Die_amx.setText("");
    cb_Die_HD.setEnabled(false);
    cb_Die_HD.setText("Die if density at the tip exceeds");
    cb_Die_HD.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cb_Die_HD_actionPerformed(e);
      }
    });
    p_Die_HD.setText("");
    cb_SB.setText("Enable seconary (internal) branching.  Sensitivity adjustment");
    pSB_SenAdj.setText("");

    cb_Die_lnmin.setText("Minimum length");
    cb_Die_lmax.setText("Maximum length");
    p_Die_lmn.setText("");
    p_Die_lnmx.setText("");
    jLabel6.setText("Probablility for the new branch to become leading:");
    label10.setText("%");
    pBrPrLeading.setText("");
    taComments.setFont(new java.awt.Font("Dialog", 0, 12));
    taComments.setText("");
    taComments.setLineWrap(true);
    taComments.setWrapStyleWord(true);
    cb_Die_remember.setText("Keep references to the dead branches");
    cb_Autotropism_c.setActionCommand("Parallel current parallel tropism");
    cb_Autotropism_c.setText("Parallel current parallel tropism");
    lbLayerThickness.setText("Layer thickness");
    cb_Autotropism_d.setText("Parallel current positive/negative tropism");
    cb_Die_nst.setEnabled(false);
    cb_Die_nst.setText("Die if the number of supported tips is less than");
    cb_Die_nst.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cb_Die_HD_actionPerformed(e);
      }
    });
    p_Die_NST.setText("");
    jLabel7.setFont(new java.awt.Font("Dialog", 1, 11));
    jLabel7.setText("Limitations:");
    cb_Die_suppose.setText("Hyphal removing supposed");
    cb_Die_suppose.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cb_Die_suppose_actionPerformed(e);
      }
    });
    jLabel8.setText("max branches");
    pMaxBranches.setText("");
    this.add(label6,
     new GridBagConstraints(1, 2, GridBagConstraints.REMAINDER, GridBagConstraints.REMAINDER, 0.0, 0.0
     ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));

    //if (FULL_VERSION)
    // {
    // Root distance depentent gravitropic angle turn. These are unlikely to be used in Oxford project.

    this.add(jTabs,                                      new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0
            ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(cbGrow,                                                  new GridBagConstraints(0, 9, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(pGrow,                                                    new GridBagConstraints(2, 9, 1, 1, 1.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(label2,                                                   new GridBagConstraints(3, 9, 2, 1, 0.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 0, 0), 0, 0));
    pnGrBr.add(label7,                                                 new GridBagConstraints(0, 10, 2, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0));
    pnGrBr.add(dGrowth,                                                 new GridBagConstraints(2, 10, 1, 1, 1.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(cb_vpl,                                                     new GridBagConstraints(0, 11, 2, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 20, 0, 10), 0, 0));
    pnGrBr.add(ch_vplMax,                                               new GridBagConstraints(4, 11, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(ch_vplK,                                              new GridBagConstraints(2, 11, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(lb_vpl1,                                            new GridBagConstraints(3, 11, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 10, 0, 10), 0, 0));
    pnGrBr.add(cbBranch,                       new GridBagConstraints(0, 1, 2, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(cb_Optimal_IBO,                      new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(cbRandomBranching,                      new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(pBrProb,                          new GridBagConstraints(2, 3, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(pBranch,                     new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(label1,                      new GridBagConstraints(3, 1, 2, 1, 0.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(cbDensityField,          new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(jLabel1,           new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(jLabel2,             new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(lb_BrAngleMax,       new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0));
    pnGrBr.add(pBrAngleMax,      new GridBagConstraints(2, 5, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(cb_SB,     new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(pSB_SenAdj,      new GridBagConstraints(2, 6, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));

    pnGrBr.add(jLabel6,        new GridBagConstraints(0, 7, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0));
    pnGrBr.add(label8, new GridBagConstraints(3, 3, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(label10, new GridBagConstraints(3, 7, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(pBrPrLeading,   new GridBagConstraints(2, 7, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnGrBr.add(jLabel8,  new GridBagConstraints(3, 6, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 4, 0, 4), 0, 0));
    pnGrBr.add(pMaxBranches,   new GridBagConstraints(4, 6, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));

    pnLimits.add(cbOld_grow,      new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.SOUTHEAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnLimits.add(pOld_grow,         new GridBagConstraints(3, 0, 1, 1, 1.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnLimits.add(label4,   new GridBagConstraints(4, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 0, 0), 0, 0));
    pnLimits.add(cbOld_branch,   new GridBagConstraints(0, 1, 2, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnLimits.add(pOld_branch,      new GridBagConstraints(3, 1, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnLimits.add(label12,  new GridBagConstraints(4, 1, 2, 1, 0.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0));
    pnLimits.add(cbLong,  new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnLimits.add(pLong, new GridBagConstraints(3, 2, 1, 1, 1.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnLimits.add(label5, new GridBagConstraints(4, 2, 3, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 0, 0), 0, 0));

    // Tropisms
    pnTropisms.add(cbAutotropism,                  new GridBagConstraints(0, 1, 4, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(lbInertia,                    new GridBagConstraints(3, 1, 3, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(cb_Autotropism_2,                  new GridBagConstraints(0, 3, 5, 1, 0.0, 0.0
            ,GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(ch_Autotropism_2,                   new GridBagConstraints(3, 3, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(taInertia,                   new GridBagConstraints(6, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 8, 0, 0), 0, 0));
    pnTropisms.add(cb_FSensorTurn,                   new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 20, 0, 0), 0, 0));
    pnTropisms.add(FSensorTurn_Angle,              new GridBagConstraints(3, 2, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(lbAngleExplain,              new GridBagConstraints(5, 2, 2, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0));
    pnTropisms.add(cb_Gravitorpism,              new GridBagConstraints(0, 10, 3, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(ch_g_angle,              new GridBagConstraints(3, 10, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(cb_g_Turn,                new GridBagConstraints(0, 11, 3, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(ch_g_Turn,              new GridBagConstraints(3, 11, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(lb_g_turn_explain,              new GridBagConstraints(5, 11, 2, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0));
    pnTropisms.add(lb_g_explain,              new GridBagConstraints(5, 10, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(ch_g_impact,               new GridBagConstraints(6, 10, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(cb_beta_turn,              new GridBagConstraints(0, 12, 3, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(l_rddg1,              new GridBagConstraints(0, 13, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 20, 0, 0), 0, 0));
    pnTropisms.add(ch_beta_turn_startAt,                  new GridBagConstraints(1, 13, 1, 1, 1.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 20, 0, 0), 50, 0));
    pnTropisms.add(l_rddg2,            new GridBagConstraints(2, 13, 1, 1, 0.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 5), 0, 0));
    pnTropisms.add(ch_beta_turn_endAt,             new GridBagConstraints(3, 13, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(l_rddg3,            new GridBagConstraints(5, 13, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(ch_beta_turn_tillValue,               new GridBagConstraints(6, 13, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(jLabel3,           new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(jLabel4,          new GridBagConstraints(0, 9, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(cb_Autotropism_3,           new GridBagConstraints(0, 5, 3, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));

    pnNeighbourhood.add(label3,       new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 8), 0, 0));
    pnNeighbourhood.add(pR_crit,        new GridBagConstraints(2, 0, 1, 1, 1.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnNeighbourhood.add(label11,    new GridBagConstraints(0, 1, 2, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnNeighbourhood.add(cb_HDF_tips,   new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0
            ,GridBagConstraints.SOUTHEAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnNeighbourhood.add(cb_HDF_bpoints,   new GridBagConstraints(2, 2, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnNeighbourhood.add(cb_HDF_all,  new GridBagConstraints(3, 2, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnNeighbourhood.add(lbChargeUnit,   new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 20), 0, 0));
    pnNeighbourhood.add(taChargeUnit,  new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnNeighbourhood.add(label9,  new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0));

    bg_Proportionality.add(cb_DirProportional);
    bg_Proportionality.add(cb_Inv_proportional);

    pnTropisms.add(cb_FSensorTurn_2,           new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 20, 0, 0), 0, 0));
    pnTropisms.add(FSensorTurn_Angle_2,         new GridBagConstraints(3, 4, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(cb_FSensorTurn_3,        new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 20, 0, 0), 0, 0));
    pnTropisms.add(FSensorTurn_Angle_3,         new GridBagConstraints(3, 6, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(jLabel5,        new GridBagConstraints(0, 14, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(cb_HPTropism,        new GridBagConstraints(0, 15, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(cb_DirProportional,       new GridBagConstraints(5, 3, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(cb_Inv_proportional,       new GridBagConstraints(6, 3, 2, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(cb_Autotropism_c,         new GridBagConstraints(0, 7, 2, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(ch_Autotropism_c,       new GridBagConstraints(3, 7, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(pHP_tropism,      new GridBagConstraints(1, 15, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(pHLayerThickness,       new GridBagConstraints(5, 15, 2, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(lbLayerThickness,       new GridBagConstraints(3, 15, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(cb_Autotropism_d,      new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(ch_Autotropism_d,      new GridBagConstraints(3, 8, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnTropisms.add(ch_Autotropism_3,  new GridBagConstraints(3, 5, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnDestructor.add(cb_Die_agmin,               new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnDestructor.add(p_Die_amn,                new GridBagConstraints(1, 4, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnDestructor.add(cb_Die_agmax,               new GridBagConstraints(2, 4, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnDestructor.add(p_Die_amx,              new GridBagConstraints(3, 4, 1, 1, 1.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnDestructor.add(cb_Die_HD,              new GridBagConstraints(0, 1, 2, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0));
    pnDestructor.add(cb_Die_lnmin,           new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnDestructor.add(cb_Die_lmax,            new GridBagConstraints(2, 5, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnDestructor.add(p_Die_lnmx,         new GridBagConstraints(3, 5, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnDestructor.add(p_Die_lmn,      new GridBagConstraints(1, 5, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnDestructor.add(cb_Die_remember,      new GridBagConstraints(0, 6, 3, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnDestructor.add(cb_Die_nst,      new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0));
    pnDestructor.add(p_Die_HD,    new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));

    jTabs.add(pnTropisms,      "Tropisms");
    jTabs.add(pnGrBr,          "Growth and branching");
    jTabs.add(pnNeighbourhood, "Neighbourhood");
    jTabs.add(pnLimits,        "Age/length limits");
    jTabs.add(pnDestructor,    "Destructor");
    jTabs.add(pnComments,      "Comments");

    pnComments.getViewport().add(taComments, null);
    pnDestructor.add(p_Die_NST,    new GridBagConstraints(2, 2, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    pnDestructor.add(jLabel7,  new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    pnDestructor.add(cb_Die_suppose,    new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
  }

  ButtonGroup bg_Proportionality = new ButtonGroup();
  GridBagLayout gridBagLayout1 = new GridBagLayout();
  GridBagLayout gridBagLayout2 = new GridBagLayout();
  GridBagLayout gridBagLayout3 = new GridBagLayout();
  GridBagLayout gridBagLayout5 = new GridBagLayout();
  GridBagLayout gridBagLayout6 = new GridBagLayout();
  GridBagLayout gridBagLayout7 = new GridBagLayout();
  JCheckBox cb_Autotropism_2 = new JCheckBox();
  JCheckBox cb_Autotropism_3 = new JCheckBox();
  JCheckBox cb_beta_turn = new JCheckBox();
  JCheckBox cb_Die_agmax = new JCheckBox();
  JCheckBox cb_Die_agmin = new JCheckBox();
  JCheckBox cb_Die_HD = new JCheckBox();
  JCheckBox cb_Die_lmax = new JCheckBox();
  JCheckBox cb_Die_lnmin = new JCheckBox();
  JCheckBox cb_FSensorTurn = new JCheckBox();
  JCheckBox cb_FSensorTurn_2 = new JCheckBox();
  JCheckBox cb_FSensorTurn_3 = new JCheckBox();
  JCheckBox cb_g_Turn = new JCheckBox();
  JCheckBox cb_Gravitorpism = new JCheckBox();
  JCheckBox cb_HDF_all = new JCheckBox();
  JCheckBox cb_HDF_bpoints = new JCheckBox();
  JCheckBox cb_HDF_tips = new JCheckBox();
  JCheckBox cb_HPTropism = new JCheckBox();
  JCheckBox cb_Optimal_IBO = new JCheckBox();
  JCheckBox cb_SB = new JCheckBox();
  JCheckBox cb_vpl = new JCheckBox();
  JCheckBox cbAutotropism = new JCheckBox();
  JCheckBox cbBranch = new JCheckBox();
  JCheckBox cbDensityField = new JCheckBox();
  JCheckBox cbGrow = new JCheckBox();
  JCheckBox cbLong = new JCheckBox();
  JCheckBox cbOld_branch = new JCheckBox();
  JCheckBox cbOld_grow = new JCheckBox();
  JCheckBox cbRandomBranching = new JCheckBox();
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JLabel jLabel4 = new JLabel();
  JLabel jLabel5 = new JLabel();
  JLabel jLabel6 = new JLabel();
  JLabel l_rddg1 = new JLabel();
  JLabel l_rddg2 = new JLabel();
  JLabel l_rddg3 = new JLabel();
  JLabel label1 = new JLabel();
  JLabel label10 = new JLabel();
  JLabel label11 = new JLabel();
  JLabel label12 = new JLabel();
  JLabel label2 = new JLabel();
  JLabel label3 = new JLabel();
  JLabel label4 = new JLabel();
  JLabel label5 = new JLabel();
  JLabel label6 = new JLabel();
  JLabel label7 = new JLabel();
  JLabel label8 = new JLabel();
  JLabel label9 = new JLabel();
  JLabel lb_BrAngleMax = new JLabel();
  JLabel lb_g_explain = new JLabel();
  JLabel lb_g_turn_explain = new JLabel();
  JLabel lb_vpl1 = new JLabel();
  JLabel lbAngleExplain = new JLabel();
  JLabel lbChargeUnit = new JLabel();
  JLabel lbInertia = new JLabel();
  JPanel pnDestructor = new JPanel();
  JPanel pnGrBr = new JPanel();
  JPanel pnLimits = new JPanel();
  JPanel pnNeighbourhood = new JPanel();
  JPanel pnTropisms = new JPanel();
  JRadioButton cb_DirProportional = new JRadioButton();
  JRadioButton cb_Inv_proportional = new JRadioButton();
  JScrollPane pnComments = new JScrollPane();
  JTabbedPane jTabs = new JTabbedPane();
  JTextArea taComments = new JTextArea();
  NumericField ch_Autotropism_2 = new NumericField();
  NumericField ch_Autotropism_3 = new NumericField();
  NumericField ch_beta_turn_endAt = new NumericField();
  NumericField ch_beta_turn_startAt = new NumericField();
  NumericField ch_beta_turn_tillValue = new NumericField();
  NumericField ch_g_angle = new NumericField();
  NumericField ch_g_impact = new NumericField();
  NumericField ch_g_Turn = new NumericField();
  NumericField ch_vplK = new NumericField();
  NumericField ch_vplMax = new NumericField();
  NumericField dGrowth = new NumericField();
  NumericField FSensorTurn_Angle = new NumericField();
  NumericField FSensorTurn_Angle_2 = new NumericField();
  NumericField FSensorTurn_Angle_3 = new NumericField();
  NumericField p_Die_amn = new NumericField();
  NumericField p_Die_amx = new NumericField();
  NumericField p_Die_HD = new NumericField();
  NumericField p_Die_lmn = new NumericField();
  NumericField p_Die_lnmx = new NumericField();
  NumericField pBranch = new NumericField();
  NumericField pBrAngleMax = new NumericField();
  NumericField pBrPrLeading = new NumericField();
  NumericField pBrProb = new NumericField();
  NumericField pGrow = new NumericField();
  NumericField pHP_tropism = new NumericField();
  NumericField pLong = new NumericField();
  NumericField pOld_branch = new NumericField();
  NumericField pOld_grow = new NumericField();
  NumericField pR_crit = new NumericField();
  NumericField pSB_SenAdj = new NumericField();
  NumericField taChargeUnit = new NumericField();
  NumericField taInertia = new NumericField();
  String cbPreState = "1";
  JCheckBox cb_Die_remember = new JCheckBox();
  JCheckBox cb_Autotropism_c = new JCheckBox();
  NumericField ch_Autotropism_c = new NumericField();
  JLabel lbLayerThickness = new JLabel();
  NumericField pHLayerThickness = new NumericField();
  JCheckBox cb_Autotropism_d = new JCheckBox();
  NumericField ch_Autotropism_d = new NumericField();
  JCheckBox cb_Die_nst = new JCheckBox();
  NumericField p_Die_NST = new NumericField();
  JLabel jLabel7 = new JLabel();
  JCheckBox cb_Die_suppose = new JCheckBox();
  JLabel jLabel8 = new JLabel();
  NumericField pMaxBranches = new NumericField();

  void cbRandomBranching_itemStateChanged(ItemEvent e) {
    if (!cbRandomBranching.isSelected())
     { cbPreState = pBrProb.getText();
       pBrProb.setText("100");
       pBrProb.setEnabled(false);
     } else
     {
       pBrProb.setEnabled(true);
       pBrProb.setText(cbPreState);
     }
  }

  public void takeFromOptions(MOptions o)
   {
      try {
        taComments.setText(
            java.net.URLDecoder.decode(o.Comments.Comment, "UTF-8"));
      }
      catch (UnsupportedEncodingException ex) {
        taComments.setText(ex.getMessage());
      }

     cbOld_branch.setSelected(o.Mycelia.old_nbranch);
     cbOld_grow.setSelected(o.Mycelia.old_ngrow);
     cbLong.setSelected(o.Mycelia.long_ngrow);

     pBranch.set(o.Mycelia.branchingDensity);
     pGrow.set(o.Mycelia.growingDensity);
     pBrProb.set(o.Mycelia.randomComponent*100);
     pBrAngleMax.set(o.Mycelia.max_Branch_angle);
     pMaxBranches.set(o.Mycelia.maxBranchesFromBranchPoint);

     pBrPrLeading.set(o.Mycelia.prBecomePrimary*100);

     pLong.set(o.Mycelia.maxSectionLength);
     pOld_grow.set(o.Mycelia.maxGrowingAge);
     pOld_branch.set(o.Mycelia.maxBranchingAge);
     pR_crit.set(Math.sqrt(o.Mycelia.criticalDistance));
     dGrowth.set(o.Mycelia.dGrowth);
     cbDensityField.setSelected(o.Tropisms.fieldHypothesis);
     cbAutotropism.setSelected(o.Tropisms.Autotropism);
     taInertia.set(o.Tropisms.InertiaFactor);

     pSB_SenAdj.set(o.Mycelia.SecondaryBranchingSensAdj);

     cbDensityField_itemStateChanged(null);
     cbAutotropism_itemStateChanged(null);

     cbGrow.setSelected(o.Mycelia.cbGrowth);
     cbBranch.setSelected(o.Mycelia.cbBranch);

     cb_SB.setSelected(o.Mycelia.SecondaryBranching);

     cb_FSensorTurn.setSelected(o.FSensorTurn.suppose);
     FSensorTurn_Angle.set(o.FSensorTurn.angle);
     cbFSensorTurn_itemStateChanged(null);

     cb_FSensorTurn_2.setSelected(o.FSensorTurn_2.suppose);
     FSensorTurn_Angle_2.set(o.FSensorTurn_2.angle);

     cb_FSensorTurn_3.setSelected(o.FSensorTurn_3.suppose);
     FSensorTurn_Angle_3.set(o.FSensorTurn_3.angle);

     cb_Gravitorpism.setSelected(o.Gravitropism.suppose);
     ch_g_impact.set(o.Gravitropism.impact);
     ch_g_angle.set(o.Gravitropism.angle);
     cb_g_Turn.setSelected(o.Gravitropism.turnSensorA);
     ch_g_Turn.set(o.Gravitropism.turn_A_angle);
     cbFSensorTurn_itemStateChanged(null);

     ch_Gravitorpism_itemStateChanged(null);

     cb_HDF_all.setSelected(o.Field.completeEvaluation);
     cb_HDF_bpoints.setSelected(o.Field.branchPoints);
     cb_HDF_tips.setSelected(o.Field.tips);

     taChargeUnit.set(o.Field.chargeUnitLength);

     cb_Optimal_IBO.setSelected(o.Mycelia.optimalBranchOrientation);

     cb_beta_turn.setSelected(o.Gravitropism.rootDistanceDependentAngleTurn);
     ch_beta_turn_startAt.set(o.Gravitropism.start_turning_at);
     ch_beta_turn_endAt.set(o.Gravitropism.complete_turning_at);
     ch_beta_turn_tillValue.set(o.Gravitropism.angle_to);

     cb_beta_turn_itemStateChanged(null);

     cb_vpl.setSelected(o.Speed_l_control.suppose);
     ch_vplK.set(o.Speed_l_control.coeff);
     ch_vplMax.set(o.Speed_l_control.maximum);

     cb_vpl_itemStateChanged(null);

     cb_Autotropism_2.setSelected(o.Autotropism_i2.suppose);
     ch_Autotropism_2.set(o.Autotropism_i2.impact);
     cb_Autotropism_2_itemStateChanged(null);

     cb_Autotropism_3.setSelected(o.Autotropism_i3.suppose);
     ch_Autotropism_3.set(o.Autotropism_i3.impact);

     cb_Autotropism_c.setSelected(o.Autotropism_c.suppose);
     ch_Autotropism_c.set(o.Autotropism_c.impact);

     cb_Autotropism_d.setSelected(o.Autotropism_d.suppose);
     ch_Autotropism_d.set(o.Autotropism_d.impact);

     cb_DirProportional.setSelected(o.Autotropism_i2.dir_prop);
     cb_Inv_proportional.setSelected(!o.Autotropism_i2.dir_prop);

     cb_HPTropism.setSelected(o.HTropism.suppose);
     pHP_tropism.set(o.HTropism.impact);
     pHLayerThickness.set(o.HTropism.layer);
     cb_HPTropism_actionPerformed(null);

     MOptions.d_Destructor D = o.Destructor;
     cb_Die_suppose.setSelected(D.suppose);

     cb_Die_HD.setSelected(D.hd_Destroy);
     cb_Die_nst.setSelected(D.st_Destroy);
     p_Die_NST.set(D.nst_min);

     cb_Die_agmax.setSelected(D.l_age_Max);
     cb_Die_agmin.setSelected(D.l_age_Min);
     p_Die_amn.set(D.min_age);
     p_Die_amx.set(D.max_age);

     cb_Die_lmax.setSelected(D.l_length_Max);
     cb_Die_lnmin.setSelected(D.l_length_Min);
     p_Die_lmn.set(D.min_length);
     p_Die_lnmx.set(D.max_length);

     p_Die_HD.set(D.hd);
     cb_Die_remember.setSelected(D.keepDeadReferences);

     cb_Die_HD_actionPerformed(null);
   }

  /** Make all values equal to the ones from the paraPanel p. */
  public void copy(ParameterPanel otherPanel, int tab)
  {
    Container p = (Container) otherPanel.jTabs.getComponentAt(tab);
    Container A = (Container) jTabs.getComponentAt(tab);

    for (int c = 0; c < p.getComponentCount(); c++) {
      Component a = A.getComponent(c);
      Component x = p.getComponent(c);
      a.setEnabled(x.isEnabled());

      if (a instanceof NumericField)
      {
        NumericField ta = (NumericField) a;
        NumericField tb = (NumericField) x;
        ta.setText(tb.getText());
      }
      else
      if (a instanceof JCheckBox)
      {
        JCheckBox ta = (JCheckBox) a;
        JCheckBox tb = (JCheckBox) x;
        ta.setSelected(tb.isSelected());
      }
      else
      if (a instanceof Choice)
      {
        Choice ta = (Choice) a;
        Choice tb = (Choice) x;
        ta.select(tb.getSelectedIndex());
      }

    }
    cbDensityField_itemStateChanged(null);
    cbAutotropism_itemStateChanged(null);
    cb_Die_HD_actionPerformed(null);
  }

  private double req(JCheckBox box, NumericField text, double def)
   {
     try {
      if (box!=null)
       if (!box.isSelected()) return def;
      double v = Double.parseDouble(text.getText().trim());
      return v;
     } catch (Exception exc)
        { exc.printStackTrace();
          return def;
        }
   }

  public void storeOptions(MOptions o)
   {
    try {
      o.Comments.Comment = java.net.URLEncoder.encode(taComments.getText(),
          "UTF-8");
    }
    catch (UnsupportedEncodingException ex) {
    }

     o.Tropisms.fieldHypothesis  = cbDensityField.isSelected();
     o.Mycelia.branchingDensity = req(cbBranch, pBranch, Integer.MAX_VALUE);
     o.Mycelia.growingDensity   = req(cbGrow, pGrow, Integer.MAX_VALUE);
     o.Mycelia.randomComponent  = req(cbRandomBranching, pBrProb, 100)*0.01;
     o.Mycelia.maxGrowingAge    = req(cbOld_grow, pOld_grow, Integer.MAX_VALUE);
     o.Mycelia.maxBranchingAge  = req(cbOld_branch, pOld_branch, Integer.MAX_VALUE);
     o.Mycelia.maxSectionLength = req(cbLong, pLong, Integer.MAX_VALUE);
     o.Mycelia.SecondaryBranching = cb_SB.isSelected();
     o.Mycelia.SecondaryBranchingSensAdj = pSB_SenAdj.get();
     o.Mycelia.maxBranchesFromBranchPoint = (int) Math.round(pMaxBranches.get());

     o.Mycelia.cbGrowth = cbGrow.isSelected();
     o.Mycelia.cbBranch = cbBranch.isSelected();
     o.Mycelia.densityDependend = o.Mycelia.cbGrowth || o.Mycelia.cbBranch;
     o.Mycelia.max_Branch_angle = pBrAngleMax.get();

     o.Mycelia.criticalDistance = pR_crit.get();
     o.Mycelia.criticalDistance = o.Mycelia.criticalDistance*o.Mycelia.criticalDistance;
     o.Mycelia.dGrowth = dGrowth.get();
     o.Mycelia.prBecomePrimary = 0.01*pBrPrLeading.get();

     o.Tropisms.Autotropism = cbAutotropism.isSelected();
     o.Tropisms.InertiaFactor = taInertia.get();

     // Field sensor turns:
     o.FSensorTurn.suppose = cb_FSensorTurn.isSelected();
     o.FSensorTurn.angle=(int) req(null, FSensorTurn_Angle, 45);

     o.FSensorTurn_2.suppose = cb_FSensorTurn_2.isSelected();
     o.FSensorTurn_2.angle=(int) FSensorTurn_Angle_2.get();

     o.FSensorTurn_3.suppose = cb_FSensorTurn_3.isSelected();
     o.FSensorTurn_3.angle=(int) FSensorTurn_Angle_3.get();

     o.Gravitropism.suppose = cb_Gravitorpism.isSelected();
     o.Gravitropism.angle=(int) ch_g_angle.get();
     o.Gravitropism.impact= ch_g_impact.get();
     o.Gravitropism.turnSensorA = cb_g_Turn.isSelected();
     o.Gravitropism.turn_A_angle=(int) ch_g_Turn.get();

     o.Field.chargeUnitLength= taChargeUnit.get();
     o.Field.branchPoints = cbBranch.isSelected();
     o.Field.tips = cb_HDF_tips.isSelected();
     o.Field.completeEvaluation = cb_HDF_all.isSelected();

     o.Mycelia.optimalBranchOrientation  = cb_Optimal_IBO.isSelected();

     o.Mycelia.old_nbranch = cbOld_branch.isSelected();
     o.Mycelia.old_ngrow = cbOld_grow.isSelected();
     o.Mycelia.long_ngrow = cbLong.isSelected();

     if (o.Field.completeEvaluation)
     {  o.Field.branchPoints=o.Field.tips=false; };

     o.Gravitropism.rootDistanceDependentAngleTurn=cb_beta_turn.isSelected();
     o.Gravitropism.angle_to=ch_beta_turn_tillValue.get();
     o.Gravitropism.start_turning_at=ch_beta_turn_startAt.get();
     o.Gravitropism.complete_turning_at=ch_beta_turn_endAt.get();

     o.Speed_l_control.suppose = cb_vpl.isSelected();
     o.Speed_l_control.coeff=ch_vplK.get();
     o.Speed_l_control.maximum=ch_vplMax.get();

     o.Autotropism_i2.suppose = cb_Autotropism_2.isSelected();
     o.Autotropism_i2.impact = ch_Autotropism_2.get();
     o.Autotropism_i2.dir_prop = cb_DirProportional.isSelected();

     o.Autotropism_i3.suppose = cb_Autotropism_3.isSelected();
     o.Autotropism_i3.impact = ch_Autotropism_3.get();

     o.Autotropism_c.suppose = cb_Autotropism_c.isSelected();
     o.Autotropism_c.impact = ch_Autotropism_c.get();

     o.Autotropism_d.suppose = cb_Autotropism_d.isSelected();
     o.Autotropism_d.impact = ch_Autotropism_d.get();

     o.HTropism.suppose = cb_HPTropism.isSelected();
     o.HTropism.impact  = pHP_tropism.get();
     o.HTropism.layer = pHLayerThickness.get();

     o.Destructor.suppose = cb_Die_suppose.isSelected();

     o.Destructor.hd_Destroy = cb_Die_HD.isSelected();
     o.Destructor.hd = p_Die_HD.get();
     o.Destructor.l_age_Min = cb_Die_agmin.isSelected();
     o.Destructor.l_age_Max = cb_Die_agmax.isSelected();
     o.Destructor.max_age = p_Die_amx.get();
     o.Destructor.min_age = p_Die_amn.get();

     o.Destructor.st_Destroy = cb_Die_nst.isSelected();
     o.Destructor.nst_min = (int) Math.round(p_Die_NST.get());

     o.Destructor.l_length_Min = cb_Die_lnmin.isSelected();
     o.Destructor.l_length_Max = cb_Die_lmax.isSelected();
     o.Destructor.min_length = p_Die_lmn.get();
     o.Destructor.max_length = p_Die_lnmx.get();
     o.Destructor.keepDeadReferences = cb_Die_remember.isSelected();
   }

  void cbDensityField_itemStateChanged(ItemEvent e) {
    boolean b = cbDensityField.isSelected();

    cbGrow.setEnabled(!b);
    label3.setEnabled(!b);
    label9.setEnabled(!b);
    pR_crit.setEnabled(!b);
    pGrow.setEnabled(!b);

    if (b)
    {
      cbBranch.setText("Branch if the field is less than ");
      label1.setText("1/distance_unit");
      label2.setEnabled(false);

      pBranch.set(MOptions.Options.Mycelia.branchingDensity);
    }
    else
    {
      cbBranch.setText("Branch only when there is less than");
      label1.setText("neighbouring tips.");
      label2.setEnabled(true);
      pBranch.setText("2");
    }

  }

  void cbAutotropism_itemStateChanged(ItemEvent e) {
    chkInertiaFactor();
    boolean b =cbAutotropism.isSelected();
    cb_FSensorTurn.setEnabled(b);
    /*
    cbDensityField.setEnabled(!b);
    if (b)
     {
       cbDensityField.setSelected(true);
     }
    cbDensityField_itemStateChanged(null);
    */
  }

  void chkInertiaFactor()
  {
    boolean b = cbAutotropism.isSelected() ||
        cb_FSensorTurn.isSelected() ||
        cb_Gravitorpism.isSelected();
    lbInertia.setEnabled(b);
    taInertia.setEnabled(b);
  }

  void cbFSensorTurn_itemStateChanged(ItemEvent e) {
   boolean b = cb_FSensorTurn.isSelected();
   FSensorTurn_Angle.setEnabled(b);
   lbAngleExplain.setEnabled(b);
   chkInertiaFactor();
  }

  void cb_HDF_all_itemStateChanged(ItemEvent e) {
   boolean b = cb_HDF_all.isSelected();
   cb_HDF_tips.setEnabled(!b);
   cb_HDF_bpoints.setEnabled(!b);
   taChargeUnit.setEnabled(b);
   lbChargeUnit.setEnabled(b);
  }

  void ch_Gravitorpism_itemStateChanged(ItemEvent e) {
    boolean b = cb_Gravitorpism.isSelected();
    ch_g_impact.setEnabled(b);
    ch_g_angle.setEnabled(b);
    lb_g_explain.setEnabled(b);
    lb_g_explain.setEnabled(b);

    cb_g_Turn.setEnabled(b);
    cb_g_Turn_itemStateChanged(e);
    chkInertiaFactor();

    cb_beta_turn.setEnabled(b);
    if (!b)
     cb_beta_turn.setSelected(false);
    cb_beta_turn_itemStateChanged(e);
  }

  void cb_g_Turn_itemStateChanged(ItemEvent e) {
    boolean b = cb_g_Turn.isSelected() && cb_g_Turn.isEnabled();
    ch_g_Turn.setEnabled(b);
    lb_g_turn_explain.setEnabled(b);
  }

  void cb_beta_turn_itemStateChanged(ItemEvent e) {
    boolean b = cb_beta_turn.isSelected();
    ch_beta_turn_endAt.setEnabled(b);
    ch_beta_turn_startAt.setEnabled(b);
    ch_beta_turn_tillValue.setEnabled(b);
    l_rddg1.setEnabled(b);
    l_rddg2.setEnabled(b);
    l_rddg3.setEnabled(b);
  }

  void cb_vpl_itemStateChanged(ItemEvent e) {
    boolean b = cb_vpl.isSelected();
    ch_vplK.setEnabled(b);
    ch_vplMax.setEnabled(b);
    lb_vpl1.setEnabled(b);
  }

  void cb_Autotropism_2_itemStateChanged(ItemEvent e) {
    boolean b = cb_Autotropism_2.isSelected();
    ch_Autotropism_2.setEnabled(b);
    cb_DirProportional.setEnabled(b);
    cb_Inv_proportional.setEnabled(b);
  }

  void cbDensityField_actionPerformed(ActionEvent e) {
    if (cbDensityField.isSelected())
    if (Double.parseDouble(pBranch.getText().trim())>1.99)
    {
      pBranch.setText("0.06");
    }
  }

  void cb_Die_HD_actionPerformed(ActionEvent e) {
    boolean b = cb_Die_HD.isSelected() || cb_Die_nst.isSelected();
    b = b && cb_Die_suppose.isSelected();
    cb_Die_agmax.setEnabled(b);
    cb_Die_agmin.setEnabled(b);
    cb_Die_lnmin.setEnabled(b);
    cb_Die_lmax.setEnabled(b);
    p_Die_amn.setEnabled(b);
    p_Die_amx.setEnabled(b);
    p_Die_lmn.setEnabled(b);
    p_Die_lnmx.setEnabled(b);
  }

  void cb_HPTropism_actionPerformed(ActionEvent e) {
    boolean b = cb_HPTropism.isSelected();
    lbLayerThickness.setEnabled(b);
    pHLayerThickness.setEnabled(b);
    pHP_tropism.setEnabled(b);
  }

  void cb_Die_suppose_actionPerformed(ActionEvent e) {
    boolean b = cb_Die_suppose.isSelected();
    cb_Die_HD.setEnabled(b);
    cb_Die_nst.setEnabled(b);
    cb_Die_HD_actionPerformed(null);
  }

}

SubstrateParameterPanel.java
--------------------------------------------------------------

package web;

import java.awt.Button;
import java.awt.Checkbox;
import java.awt.CheckboxGroup;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.util.Collection;
import java.util.Iterator;

import fungimedia_3d.Mycel;
import fungimedia_3d.Substrate;
/**
 * <p> </p>
 * <p> Simulation of the 3D growth of fungal mycelia</p>
 * <p>@author Audrius Meskauskas</p>
 * <p>@institution Vilnius Gediminas Technical university, Manchester university</p>
 * @version 2.1.0
 * @license General public license (GPL)
 */

public class SubstrateParameterPanel extends Panel {

  Growth3D master;
  Mycel m;

  Button bAdd = new Button();
  Button bChange = new Button();
  Button bRemoveSubstrates = new Button();
  Button bReturn = new Button();
  Checkbox cb_Flat = new Checkbox();
  Checkbox cb_Linear = new Checkbox();
  Checkbox cb_Negative = new Checkbox();
  Checkbox cb_Spherical = new Checkbox();
  CheckboxGroup cbg_Substrates = new CheckboxGroup();
  GridBagLayout gridBagLayout1 = new GridBagLayout();
  int selectedSubstrate = -1;
  Label label1 = new Label();
  Label label2 = new Label();
  Label label3 = new Label();
  Label label5 = new Label();
  Label lb_sp1 = new Label();
  Label lb_sp2 = new Label();
  Label lb_sp3 = new Label();
  TextField tp_Size = new TextField();
  TextField tp_X = new TextField();
  TextField tp_Y = new TextField();
  TextField tp_Z = new TextField();

  public SubstrateParameterPanel() {
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }

  private void jbInit() throws Exception {
    this.setLayout(gridBagLayout1);

    cb_Spherical.setCheckboxGroup(cbg_Substrates);
    cb_Spherical.setLabel("Spherical substrate");
    cb_Spherical.setState(true);
    cb_Linear.setCheckboxGroup(cbg_Substrates);
    cb_Linear.setLabel("Linear substrate");
    cb_Flat.setCheckboxGroup(cbg_Substrates);
    cb_Flat.setLabel("Flat substrate");
    label1.setText("Position");
    lb_sp1.setText("X");
    tp_X.setText("100");
    lb_sp2.setText("Y");
    tp_Y.setText("100");
    lb_sp3.setText("Z");
    tp_Z.setText("100");
    label5.setText("Size");
    tp_Size.setText("20");
    cb_Negative.setLabel("Negative tropism (\"inhibitory substrate\")");
    bAdd.setLabel("Add new");
    bAdd.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bAdd_actionPerformed(e);
      }
    });

    bRemoveSubstrates.setLabel("Remove all substrates");
    bRemoveSubstrates.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bRemoveSubstrates_actionPerformed(e);
      }
    });

    bReturn.setLabel("Return to the main window");
    bReturn.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bReturn_actionPerformed(e);
      }
    });
    label2.setFont(new java.awt.Font("Dialog", 1, 16));
    label2.setText("Substrate manager");
    label3.setText("Current substrates:");
    bRemoveLast.setLabel("Remove selected");
    bRemoveLast.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bRemoveLast_actionPerformed(e);
      }
    });

    tp_Attractiveness.setText("1000");
    label4.setText("Attractiveness");
    label6.setText("times more than a single hyphal tip");
    /*
    this.add(cb_Spherical,          new GridBagConstraints(0, 1, 7, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 0, 5), 0, 0));
    this.add(cb_Linear,         new GridBagConstraints(0, 2, 7, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0));
    this.add(cb_Flat,         new GridBagConstraints(0, 3, 7, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0));
    */
    lSubstrates.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        lSubstrates_itemStateChanged(e);
      }
    });
    bChange.setLabel("Change selected");
    bChange.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        bChange_actionPerformed(e);
      }
    });
    this.add(tp_X,              new GridBagConstraints(2, 4, 1, 1, 1.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    this.add(tp_Size,               new GridBagConstraints(0, 4, 1, 1, 1.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0));
    this.add(label5,              new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(8, 5, 0, 0), 0, 0));
    this.add(lb_sp2,               new GridBagConstraints(3, 4, 2, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 10, 0, 8), 0, 0));
    this.add(tp_Y,               new GridBagConstraints(5, 4, 1, 1, 1.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    this.add(lb_sp3,             new GridBagConstraints(6, 4, 1, 1, 0.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 10, 0, 8), 0, 0));
    this.add(tp_Z,                          new GridBagConstraints(7, 4, 1, 1, 1.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 100, 0));
    this.add(label1,                new GridBagConstraints(1, 3, 8, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(8, 20, 0, 0), 0, 0));
    this.add(lb_sp1,          new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 20, 0, 8), 0, 0));
    this.add(cb_Negative,             new GridBagConstraints(0, 5, 8, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    this.add(bRemoveSubstrates,             new GridBagConstraints(0, 7, 3, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    this.add(bAdd,              new GridBagConstraints(7, 7, 2, 1, 0.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    this.add(label2,           new GridBagConstraints(0, 0, 5, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    this.add(label3,           new GridBagConstraints(0, 8, 9, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(9, 0, 0, 0), 0, 0));
    this.add(bRemoveLast,       new GridBagConstraints(3, 7, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    this.add(tp_Attractiveness,     new GridBagConstraints(3, 6, 1, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 8, 0), 0, 0));
    this.add(label4,     new GridBagConstraints(0, 6, 2, 1, 0.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 20, 8, 0), 0, 0));
    this.add(label6,       new GridBagConstraints(5, 6, 4, 1, 0.0, 0.0
            ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 8, 0), 0, 0));
    this.add(lSubstrates,         new GridBagConstraints(0, 9, 10, 1, 1.0, 1.0
            ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
    this.add(bReturn,        new GridBagConstraints(4, 0, 4, 3, 0.0, 0.0
            ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
    this.add(bChange,   new GridBagConstraints(5, 7, 2, 1, 0.0, 0.0
            ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
  }

  Button bRemoveLast = new Button();
  TextField tp_Attractiveness = new TextField();
  Label label4 = new Label();
  Label label6 = new Label();
  java.awt.List lSubstrates = new java.awt.List();

  public Collection getSubstrates()
  {
    return m.substrates;
  }

  void bRemoveSubstrates_actionPerformed(ActionEvent e) {
    m.substrates.clear();
    lSubstrates.removeAll();
  }

  void bAdd_actionPerformed(ActionEvent e) {

     Substrate s=null;

     double x = gv(tp_X);
     double y = gv(tp_Y);
     double z = gv(tp_Z);
     double R = gv(tp_Size);

     if (cb_Spherical.getState())
     {
       s = new Substrate(x,y,z, R);
       s.Attractiveness = gv(tp_Attractiveness);
       if (cb_Negative.getState())
        s.Attractiveness = -s.Attractiveness;
     }
     if (s!=null)
     {
       m.substrates.add(s);
       lSubstrates.add(s.toString());
       selectedSubstrate = m.substrates.size()-1;
     }

     // move the new place
     tp_X.setText(""+ (int) (200*Math.random()-100));
     tp_Y.setText(""+ (int) (200*Math.random()-100));
     tp_Z.setText(""+ (int) (200*Math.random()-100));
  }

  double gv(TextField t)
  {
    try {
      return Double.parseDouble(t.getText());
    }
    catch (NumberFormatException ex) {
      return 0.0;
    }
  }

  void bRemoveLast_actionPerformed(ActionEvent e) {
    if (selectedSubstrate<0) return;
    if (selectedSubstrate>=m.substrates.size()) return;
    m.substrates.remove(selectedSubstrate);
    lSubstrates.remove(selectedSubstrate);
    setSelectedSubstrate(selectedSubstrate);
  }

  void setSelectedSubstrate(int p)
  {
    if (p<0) return;
    if (p>=m.substrates.size()) return;
    selectedSubstrate = p;
    if (lSubstrates.getSelectedIndex()!=p)
     lSubstrates.select(p);

    Substrate s = (Substrate) m.substrates.get(p);

    set(tp_Size, s.R);
    set(tp_Attractiveness, Math.abs(s.Attractiveness));
    set(tp_X, s.center.x);
    set(tp_Y, s.center.y);
    set(tp_Z, s.center.z);

    cb_Negative.setState( s.Attractiveness<0);
  }

  void bReturn_actionPerformed(ActionEvent e) {
    master.bReturnToModel_actionPerformed(e);
  }

  void set(TextField t, double v)
  {
    t.setText(""+v);
  }

  void lSubstrates_itemStateChanged(ItemEvent e) {
    setSelectedSubstrate(lSubstrates.getSelectedIndex());
  }

  void bChange_actionPerformed(ActionEvent e) {
    if (selectedSubstrate<0 || m.substrates.size()==0)
      return;

    Substrate s = (Substrate) m.substrates.get(selectedSubstrate);

    s.center.x = gv(tp_X);
    s.center.y = gv(tp_Y);
    s.center.z = gv(tp_Z);
    s.R = gv(tp_Size);

    s.Attractiveness = gv(tp_Attractiveness);
    if (cb_Negative.getState())
    s.Attractiveness = -s.Attractiveness;

    lSubstrates.remove(selectedSubstrate);
    lSubstrates.add(s.toString(), selectedSubstrate);
  }
  public Mycel getMycelia() {
    return m;
  }
  public void setMycelia(Mycel m) {
    this.m = m;
    lSubstrates.removeAll();
    Iterator iter = m.substrates.iterator();
    while (iter.hasNext()) {
     lSubstrates.add(iter.next().toString());
    }
  }

}

--------------------------------------------------------------
This concludes the program listing for subdirectory C:/neighbour-sensing_model/fungi/src/web
--------------------------------------------------------------

This concludes the Program listings for the Neighbour-Sensing model of hyphal growth

--------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>  Everyone is permitted to copy and distribute verbatim copies  of this license document, but changing it is not allowed.

Preamble

  The GNU General Public License is a free, copyleft license for software and other kinds of works.

  The licenses for most software and other practical works are designed to take away your freedom to share and change the works.  By contrast, the GNU General Public License is intended to guarantee your freedom toshare and change all versions of a program--to make sure it remains free software for all its users.  We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors.  You can apply it to your programs, too.

  When we speak of free software, we are referring to freedom, not price.  Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights.  Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received.  You must make sure that they, too, receive or can get the source code.  And you must show them these terms so they know their rights.

  Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software.  For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.

  Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so.  This is fundamentally incompatible with the aim of protecting users' freedom to change the software.  The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable.  Therefore, we have designed this version of the GPL to prohibit the practice for those products.  If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary.  To prevent this, the GPL assures that patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and modification follow.

TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this License.  Each licensee is addressed as "you".  "Licensees" and "recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy.  The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based on the Program.

  To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy.  Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other parties to make or receive copies.  Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License.  If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work for making modifications to it.  "Object code" means any non-source form of a work.

  A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form.  A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities.  However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work.  For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

  The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

  The Corresponding Source for a work in source code form is that same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met.  This License explicitly affirms your unlimited permission to run the unmodified Program.  The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work.  This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force.  You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright.  Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under the conditions stated below.  Sublicensing is not allowed; section 10 makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

  When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7.  This requirement modifies the requirement in section 4 to "keep intact all notices".

    c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy.  This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged.  This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

  A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit.  Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

    a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.

    d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge.  You need not require recipients to copy the Corresponding Source along with the object code.  If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source.  Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling.  In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage.  For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product.  A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source.  The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

  If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information.  But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

  The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed.  Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law.  If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it.  (Additional permissions may be written to require their own removal in certain cases when you modify the work.)  You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or authors of the material; or

    e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

  All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10.  If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term.  If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly provided under this License.  Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

  However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

  Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License.  If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or run a copy of the Program.  Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance.  However, nothing other than this License grants you permission to propagate or modify any covered work.  These actions infringe copyright if you do not accept this License.  Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License.  You are not responsible for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations.  If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License.  For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based.  The work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version.  For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement).  To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

  If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients.  "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

  A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License.  You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License.  If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all.  For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work.  The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time.  Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

  Each version is given a distinguishing version number.  If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation.  If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

  Later license versions may give you additional or different permissions.  However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

END OF TERMS AND CONDITIONS

How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>  Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.
--------------------------------------------------------------

  If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License.  Of course, your program's commands might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program into proprietary programs.  If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library.  If this is what you want to do, use the GNU Lesser General Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--------------------------------------------------------------

--------------------------------------------------------------
This concludes the file listings for subdirectory compensation/src/

This concludes the Program listings for the Neighbour-Sensing model of hyphal growth

Program listings for the model using curvature compensation to simulate the spatial organisation of the gravitropic reaction
Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

Limitation of Liability.
  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

Program and coding Copyright (C) Audrius Meškauskas 2017
Text Copyright (C) David Moore 2017
All other rights and trademarks of third parties acknowledged
--------------------------------------------------------------

Directory Structure

C:/compensation
C:/compensation/.settings
C:/compensation/src

--------------------------------------------------------------
Program listings
--------------------------------------------------------------

Listing of two files in directory /compensation/
.classpath & .project

.classpath Listing
--------------------------------------------------------------

<?xml version="1.0" encoding="UTF-8"?>
< >
	<classpathentry kind="src" path="src"/>
	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
	<classpathentry kind="output" path="bin"/>
</classpath>

.project Listing
--------------------------------------------------------------

<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
	<name>compensation</name>
	<comment></comment>
	<projects>
	</projects>
	<buildSpec>
		<buildCommand>
			<name>org.eclipse.jdt.core.javabuilder</name>
			<arguments>
			</arguments>
		</buildCommand>
	</buildSpec>
	<natures>
		<nature>org.eclipse.jdt.core.javanature</nature>
	</natures>
</projectDescription>

--------------------------------------------------------------
This concludes the file listings for directory /compensation/
--------------------------------------------------------------

Listing of one file in subdirectory /compensation/.settings/
org.eclipse.jdt.core.prefs

org.eclipse.jdt.core.prefs Listing
--------------------------------------------------------------

eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8

--------------------------------------------------------------
This concludes the file listings for subdirectory /compensation/.settings/
--------------------------------------------------------------

Program listing of 21 .java files and 3 .txt documents in directory compensation/src/

Listings are arranged in alphabetical order

Bank.java
ConfigBank.java
DataBank.java
DataSetControl.java
DataSize.java
Experimental.java
gpl-3.0.txt <GNU GENERAL PUBLIC LICENSE>
GraviJa.java
LayoutHelpers.java
LPK.java
Main.java
model parameters.txt
ModelCanvas.java
ModelControls.java
MyButton.java
ParaBank.java
ParaSetControl.java
RightControls.java
SolvingThread.java
TModelBase.java
TModel.java
TopControls.java
wheat.txt
XDataSetControl.java
--------------------------------------------------------------

Bank.java Listing
--------------------------------------------------------------

import java.awt.BorderLayout;
import java.awt.Checkbox;
import java.awt.Event;
import java.awt.Panel;
import java.awt.TextArea;

@SuppressWarnings("serial")
class Bank extends Panel {
	public GraviJa app = null;;

	public String n;

	public TextArea t;

	public Bank() {
	}

	public Bank(String name, String data) {
		setLayout(new BorderLayout());
		add("Center", t = new TextArea(data, 12, 24));
		n = name;
	}
	public boolean action(Event ev, Object arg) {
		if (ev.target instanceof Checkbox) {
			// String label = (String)arg;
			app.controls.resolve();
			return true;
		}
		return false;
	}
}

ConfigBank.java Listing
--------------------------------------------------------------

import java.awt.Checkbox;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Label;

/**
 * Controls the set of configuration settings.
 */
@SuppressWarnings("serial")
class ConfigBank extends Bank {
	public GridBagLayout bag;

	public GridBagConstraints C = new GridBagConstraints();
	public Checkbox Hyp_Apical = new Checkbox("Apical perception");
	public Checkbox Hyp_Auto = new Checkbox("Straightening reaction");;

	public Checkbox Hyp_Local = new Checkbox("Local perception");;

	public Checkbox Hyp_SigDec = new Checkbox(
			"Decrement of the percepted signal");
	public Checkbox Sh_Exp = new Checkbox("Experimental data");
	public Checkbox Sh_Mod = new Checkbox("Model prediction");
	public Checkbox Sh_Tip = new Checkbox("Apex angle only");
	public ConfigBank() {
		setLayout(bag = new GridBagLayout());
		C.gridwidth = GridBagConstraints.REMAINDER;
		C.anchor = GridBagConstraints.WEST;
		al("Suppose:");
		ad(Hyp_Apical);
		ad(Hyp_SigDec);
		al("during transmission");
		ad(Hyp_Local);
		ad(Hyp_Auto);
		al(" ");
		al("Show:");
		ad(Sh_Exp); // Sh_Exp.setState(false);
		ad(Sh_Mod);
		Sh_Tip.setState(false);
	}
	public void ad(Checkbox k) {
		add(k);
		k.setState(true);
		bag.setConstraints(k, C);
	}

	public void al(String s) {
		Label l = new Label(s);
		add(l);
		bag.setConstraints(l, C);
	};

	public void Reset() {
		Sh_Mod.setState(true);
		Hyp_Apical.setState(true);
		Hyp_SigDec.setState(true);
		Hyp_Local.setState(true);
		Hyp_Auto.setState(true);
		Sh_Tip.setState(false);
		Sh_Exp.setState(true);
	};
}

DataBank.java Listing
--------------------------------------------------------------

import java.awt.Label;

@SuppressWarnings("serial")
class DataBank extends Bank {
	public DataSize nt;

	public DataBank(String name, String data) {
		super(name, data);
		add("North", new Label("Format: <time> <angle> <stdev of mean>. "));
		add("South", nt = new DataSize("Stems measured:", 12));
		n = name;
	};
}

DataSetControl.java Listing
--------------------------------------------------------------

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Choice;
import java.awt.Event;
import java.awt.Panel;

/**
 * Controls the data set.
 */
@SuppressWarnings("serial")
class DataSetControl extends MyPanel {
	
	DataSetControl partner = null;
	public Bank Data[] = new Bank[20];
	String DataOnTop = "Raw init";

	public Choice DataSet = new Choice();
	int DI = -1; // Paskutinio banko indeksas, -1 jei nieko nera
	public Panel Dp = new Panel();
	CardLayout l = new CardLayout();
	boolean restarting = false;
	public DataSetControl() { // Bicho DataSet tokia pati ir junginejasi taip
								// pat
		setLayout(new BorderLayout());
		add("North", DataSet);
		add("Center", Dp);
		Dp.setLayout(l);
		AddAllData();
		l.show(Dp, Data[0].n);
		DataOnTop = Data[0].n;
	}
	public DataSetControl(GraviJa _app) { // Bicho DataSet tokia pati ir
											// junginejasi taip pat
		setLayout(new BorderLayout());
		add("North", DataSet);
		add("Center", Dp);
		Dp.setLayout(l);
		AddAllData();
		l.show(Dp, Data[0].n);
		DataOnTop = Data[0].n;
		_app = app;
	}

	public boolean action(Event ev, Object arg) {
		if (ev.target instanceof Choice) {
			String label = (String) arg;
			// Action:
			l.show(Dp, label);
			DataOnTop = label;
			partner.DataOnTop = label;
			partner.DataSet.select(label);
			partner.l.show(partner.Dp, label);
			app.controls.resolve();
			return true;
		}
		return false;
	};

	void AddAllData() {
		AddData("Dummy data set...", "");
		AddData("Triticum aestivum coleoptiles", "");
		AddData("Coprinus Cinereus stems", "");
		AddData("User defined data", "");
		AddData("Help on data definition", "");
	};

	public void AddData(String name, String data) {
		if (restarting) {
			Data[++DI].t.setText(data);
		} else {
			Data[++DI] = getBank(name, data);
			if (DataSet != null)
				DataSet.addItem(name);
			if (l != null)
				l.addLayoutComponent(name, Data[DI]);
			if (Dp != null)
				Dp.add(Data[DI], name);
		}
		;
	};

	void appendText(String s) {
		top().t.append(s);
	};

	public Bank getBank(String name, String data) {
		return new Bank(name, data);
	};

	String getText() {
		return top().t.getText();
	};

	public boolean keyDown(Event e, int k) {
		if (k == Event.F9) {
			app.controls.resolve();
			return true;
		}
		;
		return false;
	};

	public void Reset() {
		DI = -1;
		restarting = true;
		AddAllData();
	};

	Bank top() {
		int k;
		for (k = 0; k <= DI; k++) {
			if (Data[k].n.compareTo(DataOnTop) == 0)
				return Data[k];
		}
		;
		return null;
	};

}

DataSize.java Listing
--------------------------------------------------------------

import java.awt.Color;
import java.awt.Label;
import java.awt.TextField;

@SuppressWarnings("serial")
class DataSize extends MyPanel {
	public void setNt(int _Nt) {
		nt.setText(Integer.toString(_Nt));
	};

	public TextField nt;
	public Label Zyme;

	public DataSize(String objName, int _def) {
		setBackground(Color.lightGray);
		add(Zyme = new Label(objName));
		add(nt = new TextField(Integer.toString(_def)));
	};

}

Experimental.java Listing
--------------------------------------------------------------

/**
 * Defines data that come from real mycological experiments.
 * 
 * @author Audrius Meskauskas
 */
public class Experimental {
	public String C_air() {
		return new String(
				">;_<_;_<_<_<_<^<^=_<^=]<^=^=]=^=]>]=]>]=]>!>]>!>!>!>!?!>[?!>[?!?\n"
						+ "=<_<_;_<^<_<^<_<^=^<^=]<^=^=]=^=]=]=]=]>]=!>]>!=]>!>!>!?!>!>[?!>\n"
						+ "<=_<_<_;_<_<_<^=_<^=^<^=^=]=^=]=]>^=]>!=]>]>!>!>!?!>!?!?[>!?[?[@\n"
						+ "=<`<`;`<_<_=_<_=_<^=_=^=^>^=]>^>]>]>]>!?]>!?!?!?!?[@!?[@[@[@Z@[A\n"
						+ ";>b;a;a;`<a<_=`<`=_=_=^>^=^>^?]>^?!?]?!@!?!@[@!AZA[@ZBZAZBYBYBYB\n"
						+ ":>d;c;c<b;b=a<a=a>`>_>_>_?^@^?]A]@!A!A!B[BZBZCZDYCYDXEXDXEWFVFVF\n"
						+ ">;f:e;d;d;c<b=b=a>a>`>`@^?_A]@]B]A!C[BZDZDZDYEXEWFWGWGVGUHTITITI\n"
						+ ":?f:e;e;d<d<c=b=b>`?a?_@_@^A^B!B]B[D[CZEYEYEXFXGVGWHUHUITJSJSKRK\n"
						+ "9@f:f;e<e<c<c>c>a>a?`@`A_A^A]C!B!D[D[EZEYFXFXGVHWIUIUITJSKSKQLRM\n"
						+ "@:g:g;f;e<d=c=c?b>a@`@_A_B^B]C!C!EZEZEYGYGWGWIVIUJTJSKSLRLQMPNPO\n"
						+ "?:h:g;f<f<d=d>c>a?a@aA_A_B]C]D!D[EZFZFXGXHWIVIUKTJSLSLRMQNPOOONP\n"
						+ "?:i:g;f<f<e>c>c>b@a@`B_A_C]D]D[E[FZFXHXHWHWJUKTKTLRLRNQNOOOPNQMQ\n"
						+ "?:h;g;f<f=d=d?b?b?aA`A_B_C]D!D!E[FZGYGXHWIVIVKTKTLSLQNQNQOOPNPNQ\n"
						+ "A9h:h;f=f<d>d?c?a@aA`A_C^C]D]E[EZGZGXHXIWIUKUKTLSMRMQNPPOOOQMRMR\n"
						+ "B8i;g;g=f=d?d?c@aAaA_C_D]D]E[F[GYHXIXJVJVKTMSMRNROPOOQNQMSMSKTJU\n"
						+ "D7h;h;f=f>e?c?c@aB`B`C^C^E!F[FZHYHYIWJVKULTMSMROQOPPNQNRMSLTJUJU\n"
						+ "E5i<g<g=e>e?c?bAbB`B_C^E]E!F[GZHYIXIWKUKUMTMRNROPPOQORMSLSKUKUIV\n"
						+ "G3i<g=f>e>d@c@bAaB`C_D^E]F!F[HYHYIXKVKVLTLTNROROPPORORMSLTKUJUIW\n"
						+ "E5i<g<g>e?d?c@bBaB`C_D^E!F!GZHZHXJWKVKVLTNSNQOQPPQORMSMSKUJVJVHW\n"
						+ "I2i;g=g=f>d?c@bAbB_C_D^E!F!GZHYIXJWJULUMSNRNQPPQOQNSLSLUJUIWHWGX\n"
						+ "J1i<g<g>e>e@c@aBaC`C^E]E!G[GZIXIXKVKUMTMSOQOPQPQNSMSKUKUIWHWGYFY\n"
						+ "N.h<g>f>e@cAbBaC`D^E]F!G[HYIXKWKVLUNSNRPPQPRNRMTLUKVIWHXGYE[E[C!\n"
						+ "O,h>g>e?dAcBaBaD_E^E!G!HZIYJXKVLVMTNSOQQPQPRNTLTLVJVIXHYFZEZD!C]\n"
						+ "O-g=g?e@cAcAaC`D_F]F]G[HYJYJWLVLUNSOROQQPRNSMULUKVIWHYGYF[D[C]B^\n"
						+ "N.h=f>e?dAcAaC`D^E]F!G[HYJXKVKUMTOSOQPPRNRMTLUKVIWHXGZEZD!B]A^@_\n"
						+ "Q+g>e?eAbAbC`D_E^F!G[HYJYKWKUMUNSOQQPQOSNSLUKVJWHYGYFZD!C]B^@_@`\n"
						+ "O-f>f@cAcBaC_D_E]G!HZIYJXKVLUNTNRPQQOROSLTLVJWIWHYFZE!C!B^A^?`>a\n"
						+ "P,f>eAcAbBaD_D^F]G[HZJXJWLVMUNSOQPQQOSMTMUKVIWHXGZFZD!C]A^@`?`>b\n"
						+ "P,f?e?cBaBaD_D]F]GZIZIXKVLVMTNROQQPRNSMTLUJWHXHXF[D[D!A^A_?`=a=b\n"
						+ "R*f?dAbBaC`D_E]G[H[HYJWLWLTNTNRPQQOSNSMUKUJWHXGZFZD!C]B^@_?`>b<b\n"
						+ "S)e@d@bBaC`D^F]F!HZIYJWKVMUNSORPPQORNTLUKVIWHXGZE[D!B]A^@`>`=b<c\n"
						+ "R*e@cAbBaD_D^F]G[HZIXKWKVMTNSOQQPQOSMTLUKVIXHXFZE[D]B]A_?_>a=c;c\n"
						+ "S*d@cAbC`C_E]G!G[HZJWKWLUMTOROQQPRNSMUKUJWIXGYFZE!C!B^@_?`=b=b:d\n"
						+ "T)d?cAbB`C^E^E[G[IXIXKVLTMSOQPPQORMSKUKVHXGXFZD[C!B^@^>a=a;b:d9e\n"
						+ "T(dAbB`C_E^E!G[HZJXJWLUMTNROQQOQOSLULUJWHWGYF[D[C]A^@_?`=b;b;d9e\n"
						+ "T(dAbB`C_D^F]FZHZIXKVKVMSNSPQPORNSLTLUIWIXFYFZD!B]A^@_>`=b;c:d9e\n"
						+ "T(cAbC`C^E^F[G[HYJWKWLTMSNRPQQORMSLUKVIWHXGZEZC!B]A_?_>a<b;c:e8e\n"
						+ "U'cBaC`C^F]F[GZIYJWKVMTMSOQPQRNRNTKUKVIWGXGZD[D!B]@_?`>a<b;c:e8f\n");
	};

	public String C_Water() {
		return new String(
				"==]<^>^=^=^=^=]>^=]>^=]>^>]>]=^>]>]>]>]>]?]>]>]>]?]>!?]>]?]>!?]>\n"
						+ "==^=]=^=^=^=^=^>]=^>^=]>^=]>]>^>]>]>]>]>^>]>!?]>]>]?]?!>]?!?]?!?\n"
						+ "==^<^=^=^=^=^=^=^=^=]>^=^>]=^>]>]>^>]>]>]>]?]>!?]>!?]?!?]?!@!?!@\n"
						+ "==^<^=_=^<^=^=^=^>^=^=]>^=^>]>]>^>]>]>]>]?]>]?!?]?!?]?!?!@!?!@[@\n"
						+ "==^<_<^=^=^=_=^=]=^>^=^>]=^>]>]>^>]>]?]>]?!>]?]?!?]?!?!?!@!?]@[@\n"
						+ "==^<^=_=^=^=^=^=^=^>^=^>]=^>]>^>]>]>]?]>]?]?]>!?]?!@!?]?!@!@!?[@\n"
						+ "==^<^=_=^=^=^>^=^>^=^>]>^>]>^>]>]>]?^>!?]>]?]?]?!@!?]?!@!?!@!@!@\n"
						+ "==_<_<_=_=_=_=^>_=^>^>^>^>]?^>]?]?]@]?]@!@]@!@!@!A!A[A!A[B[A[B[B\n"
						+ "==`;`<`=_<`=_=_=_>^>_>^>^?^?]?]@]?]@]A!@!A!A!B!A[B[B[CZC[CZCZDZC\n"
						+ "==`;a<`=`<`=_>_=_>_?^>_?^?]@^?]A]@]A!A!A!B!B[B[C[C[CZDZDZDZDYEYE\n"
						+ "==a;`<a=`=`=`=_>_>_?_?^?^@]@^@]A]A!A]B!B[B!C[C[DZDZDZDZEYEZFXFYF\n"
						+ "==a;a<a=a<`>`=_>`?_>^@_?^@]A^@]B!A]B!C[B!D[C[DZEZDZFYEYFYGYFXHXG\n"
						+ "==a;b<a=a=`=`>`>`?_?^?_@^A]@]B]A]C!B!C[D[D[D[EZEYFZFYFXGXHXHXHWI\n"
						+ "==b;b;b=a<a>a>`>`?_?_@^A^A]B]B]B!C!D[D[E[EZFYFZGXHYGXIWIWIWJVKVK\n"
						+ "==b;c<b<b=a>`>`?`?_@_A^A^A]C]C!C!D!E[EZEZGZGYGYHXIXIWIWKVJVLULUL\n"
						+ "==c:c;b<b=b=`>a?`?_@_@^B^A]C]C!C[E!EZE[FYGYGYHXIXIWIWKWKUKVLUMTM\n"
						+ "==c:c<b<b=a>a>`?`?_@_A^B^B]B]D!C[E!EZFZFZGYHYHXIXIWJVKWKULVLTMUN\n"
						+ "==d:c;c<b=b>a?`?`@_A^B^B]C]D!E[E[F[GYGZHXIXJXJWJWLVKVMUMTNUNSOTO\n"
						+ "==d9e;c<c<a>a?a?_@_B^B^C]D!D[E[G[FZHYHXIYJWJWKWKVLVMUMUMTNTOTOSO\n"
						+ "==d9d;c<c=a>a>`@`@_A^B^C]C!D!E[FZF[GYHYHYIXIXJWKWKVLVLVLUMUMUNTN\n"
						+ "==d:d;c<c=a>a>`@`A^A_B]C]D!E!E[FZFZHZHYHXJXIWKXKVKVLVLVMUMUNTNTO\n"
						+ "==d:d<c=c>a?a@`A_B^C]C]E!F!F[GZHYIYJXJXKWLVLVNVMUOTOTOTPSQRQSRRR\n"
						+ "==d;d<c=b>b@`@`A_C^C]D]F[F!GZGZIYJXJXKWLWLVMVNTOUOTPSPSQSRRRQSRS\n"
						+ "==d;d<c=b?a@a@_B_C^C]E!E!G[GZHZJXJXJXLWLVMUNUOUOTPSQSQRRRSQSQTPT\n"
						+ "==d;d<c=b?b@`@_B_C^D]E!F[F[HZIYIXKXKWLVMVNUOUOTPSQSQRRRSQSPTQUOU\n"
						+ "==d;d<c>b>a@aA_B^C^D]E!G[GZHZIYJXKWKWMVMUOUOTPTPSQRRRSQTQTPTPVOU\n"
						+ "==e:d<c=b?a?aA_B_B]D]E!F[HZHZIYJXKWKVMVNUNUOTPSQSRRRQSQTPTPUOUOW\n"
						+ "==e:d<c=b?a?aA_B^C^D]E!F[GZHZIXJXKXLVMVMUOUOTPSQRQRSRSQSPUPUOUOW\n"
						+ "==e:e<c=b>a@`A_C^C]E!F[GZIZIXKXKWLVNUNUOTPSQSQRSRSQSPUPUPUOVNWOW\n"
						+ "==e9e<d<b?a@`A_B^D]E!F[GZIYJXJXLVMVMUOUOTQSQRRRRRSPTQTPUOVPUOWNV\n"
						+ "==f9e;d<b>a@`A_C]D]F[G[HYIXJXLWMUMUOUOSQSQSRQSQSQTPUPUOVOVOVNWNW\n"
						+ "==f9d<d=b?`@`B^C]E]F[HZHYJXKWLVNUNUOTPSQSRRSQSQTPUPUOVOVOWNWNXNW\n"
						+ "==f9e;c=b?a@`B^C]E!F[HYIYJXKVMVNUOTOTQSQRSQSQTPTPUOVOVOWNWNWNXNX\n"
						+ "==f9d<c>b?`A_B^D]F[G[HYIXKXLVNUNUOTQSQRRRSQTPTPUPVOVOWNWNWNXNXMX\n"
						+ "==f9e;c=b?`A`B^C!F!FZHZJXJWLWMUNUPTPSQRRRSQTPTPUOVOVOWNWNWNXMXMY\n"
						+ "==e:d<c>b?_B_B^E!F[GZIXJXLWLVNTOTPSQSSQRQTQUOUOVOWNWNXMXMYMYMYLZ\n"
						+ "==e:d<c>a@`A_C]E!F[HZIXJXLWMUNUOTPSRRRRSPTQUOUPVNWNWNXNXMYMYLYMY\n");
	}

	public String C_CyD_1() {
		return new String(
				"==]<]=^=]=]=^=]=]=^=]>]=^=]=]=]=^=]=]=]>^=]=]=]=]>^=]=]=]>]=]=]=\n"
						+ "==!=]>]>]>]>]>]=]>]=]>]=]>^=]=]>^=]=]=^=]>^=]=^=]=]=^=]=^=]=^=]>\n"
						+ "==]=]=]=]>^=]=]>]=^=]>]=]=^=]>]=]=^>]=]=^=]>]=]=^=]>]=]=^=]>]=]=\n"
						+ "==!=]>]=]>^>]=]>]=]>]=]=^>]=]=]>^=]=]=^>]=]=^=]=]>^=]=^=]=^=]=]=\n"
						+ "==]=^=]=^=]>^=]=]=^>]=^=]>^=]=]>^=]>]=]>^=]>]>]=]>]>]>]>]>]>]>!>\n"
						+ "==^<^=^=]=^=^=^=^=]>^=]>^=]>]=^>]>]>]>]>]>]>]>]>]?]>!>]?]?!>]?!?\n"
						+ "==^<^=^=_=^=^=^=]=^>^=^>]=^>]>]>^>]>]>]?]>]?!>]?]?!?]?!?!?!@]?!@\n"
						+ "==^<_<_=^=_=^=^=^=^>^=^>^>]>^>]>]?]>]?]?]?]?!?]@!?!@!@!@!@!@[A!@\n"
						+ "==`;_<`<`<_=_=_=_=^=^>_>]>^?^?]>]@]?]@!@!@]A[@!A!A[B[B[B[BZBZCZC\n"
						+ "==`;a;`<`=_<`=_=_>_=^?^>^?^>]@^?]@!@]A!A!A!A!B[B[B[BZC[CZDZCYDZE\n"
						+ "==`;a<a<`<`=_=`>_=^?_>^?^?^?]@^@!A]@!B]A[B!B[B[C[C[DZCZDYEZEYEYE\n"
						+ "==a;a<a<`=`=`=_>`>_>^?^?^@^@]@^A!A]B!A!C[B!C[CZD[DZEZDYFYEYFYFXG\n"
						+ "==a;b<a<`=a=`>_>`>_?^?_@^@]@]A]B]A!C!B!C[D[C[EZDZEZFYEYGYFXGXHXH\n"
						+ "==b;a<b<a=`>`=`?`?_?^@^@^@^A]B]B!C!B!D[D[D[EZEZEYGZFXGYGXHXHWIWI\n"
						+ "==b;b<a=a=a>`>`?_?_@_@^A^A]B]B]C!C!D[D[D[EZFZFZFYGYHXHXHXIWIWJWJ\n"
						+ "==b;b=b=a=a>`?`?_?_A_@^B^A]C]B!D!D!D[E[EZFZGZGYGXHYHXIWJWJWJVKVK\n"
						+ "==b<b<b>a>a>`?`@_@_A_A^B]B]C]C!D!E[E[F[FZFYGZHYHXIXIXJWJWKVKVLVL\n"
						+ "==b<b=b=a>a?a@_@`@^A_B^B]C]D]D!D[F[E[GZFZHYHYHXIXJXJWJWKVLVLVMUM\n"
						+ "==b<c=a>b>a?`@`@_A^B_B]C^C]D!E!E[F[FZGZGZHYIXIXJXJWKWKVLVMVLUNUN\n"
						+ "==b<c>a>b?`?`A`@_B_B^B]D]D]D!E!F[GZF[HYHYIYIYJWJXKWLVLVLVMUNUNTO\n"
						+ "==c;c=b>b?a?`@`A_B^C^C]D!E!E!F[GZGZHZIYIYIXJXKWKWKWLVMWLUMVNUNUN\n"
						+ "==c;d=b>b?a?`@_B_B_B]D]D]E[F!G[GZHYHZIXJYJXJWLWKWLVMVMVMUNUNUOUO\n"
						+ "==c;d=b>b?a@`A_A_C^C^D!E]E[G[G[GZIYIYIYJXKWKXLVLWMVMVNUMUOUNTOUP\n"
						+ "==d<c=b>b?a@`A_A_C^C]E]E!F!F[GZHZIYIYJXKXKWLWLVMVMVNVNUOTOUOTPTP\n"
						+ "==d<c=b>b?aA`A_B_C]D^D!F!F[G[HZIYIYJYKXKWLWLVMVNVNUNUOUPTOTPSQTQ\n"
						+ "==d;d<c>b>a@aA_B^C^D]E!F!G[HZHYIYJYKXKWLWLVMVMVNVOUNTOUPTOTPTPTQ\n"
						+ "==e;d<c>b>a@`B_B^C^E]E!F[G[HZIYJXJYKWLWLWMVNUNVNUOTPUPTPSPTQTPSQ\n"
						+ "==e;d<c>b?a@`B_C^D]D]F[G[HZIZIYKXKWLXLVMVNVNUOUOUPTPTPTQSQTQSQSQ\n"
						+ "==e:d=c>b?a@`B_C^D]E!F!G[HZIYJXKXLWLWMVNVNUOUOTPUPSQTQSQSRSRSQSR\n"
						+ "==e;d<c>b@a@`B^C^D]F!F!GZIZIYKXKXLWLVNVNVOTOUPTQTPSRSQSRRSRRSSRS\n"
						+ "==e;d=b>b@aA_B_C^E]F!F[HZIZJXJYLWLWMVNVOUOTPTPTQSQSRSSRRRSRSRTQT\n"
						+ "==e;d=c>a?aA`B^D^D]F!G[HZIYJXKXLWMWMVNUOUPTPSQTRRRSRRSRTQSQTQTQU\n"
						+ "==d;d=c?a@aA_C_C]E]F!GZHZIZKXKXLWMVMVOUOTPTQTQSRRSRSRSQTQTPUQUPV\n"
						+ "==d;d>c>a@aB_B^D^E!F!GZIZIYKXKXLWNVNUOUOTQTQSQSSRSQSRTQTPUQUPVOU\n"
						+ "==e;d=b>b@`A`B^D^E!F[G[IZIYKXLWLVNVNUOUPSQTQRRSSQSRTQTPUPUPVPVOV\n"
						+ "==d;d>b>bA`A_C_D]E!F[H[IYJYJXLWMVNVNUPTPTQSQRSRSRSQUQTPVPUOVPWNW\n"
						+ "==d;d=c>b@`@`B_C^D]F!F[G[IZIYJXKWLWMVNVOUOTPTQSQSRRRRSQTQTQUPUPU\n"
						+ "==d<c>c?a@`B_C^E]E!G[HZIYJYKWLWNVNVNTPTQTQSRRSRTQTPUQUOVPVOWOWNX\n"
						+ "==d<c>b@a@`C_C]E]F!G[HZJYJXLWLWNVNUOUQSQTQRSRSRTQTPUPVPVOVOWOXNW\n"
						+ "==d<c>c?a@`C^C^E]F[G[HZJYKXKWMWNUNUPTPTQSRRSRSRTPUQUPVOVPWNWOWNX\n"
						+ "==d<c>c?aA_B_C^E!F!G[IYIYKXLWMWMUOUPTPTRRRSSQSRTPUPVPUPWOWNWOWNX\n");
	};

	public String C_CyD_10() {
		return new String(
				"==!=]>]=]=]>]=]>]=]=]>]=]=]=^=]=]=]=^=]=]=^=]=^=]=^<]=^=]<^=^<]=\n"
						+ "==!=]>]=]>]=]=]>]=]=^=]=]=]=^=]=]=^=]=]=^=]=^=]<]=^=]=^=]=^<]=]=\n"
						+ "==!=]=]>^=]=]>]=]=]=]>]=^=]=]=]=]=^=]=]=^=]=]=^=]=^=]=^=]<]=^=]=\n"
						+ "==]=]=]=^=]=]=^=]=]=^=]=]=^=]=]=^=]=]=]=^>]=]=]=^=]=]>]=]=]=]>]=\n"
						+ "==]=]=^=]=]=^=]=]>^=]=]=^=]=]=^>]=]=^=]=]>]=^=]>]=]=]>]=]>]=]>]=\n"
						+ "==]<^=]=^>]=^=]=^=]=]=^>]=]=^>]=]=]>^=]>]=]>]=]>]=]>]>]=]>]>]=]>\n"
						+ "==]<^=]>^=^=]=^=]=^=]>^=]=]>^=]=]>^=]>]=]>]>]=]>]>]>]>]>]>]>]>!>\n"
						+ "==]<^=^=^=]=^=]=^>^=]=^=]>]=^=]>]=^>]>]=]>]>]>]>]=]>]?]>!>]>]>!?\n"
						+ "==^<^<^=^=^<^=^=^=^=]>^=]=^>]=^>]>]=]>]>]>]>]?]>!>]?!?]>!?]?!?!?\n"
						+ "==^<^<_=^=^<^=^=^=^>^=]=^>]=^>]>]>]>]>]?]>]>]?!?]>]?!?!?!?]@!?!@\n"
						+ "==^<_<^=_<^=^=^=^=^=^>^=^>]=]>^>]>]?]>]>]?]?!>]?!?!@]?!?!@!@!@[?\n"
						+ "==^<_<^=_=^=^=_=^=]=^>^>^=]>]>^>]?]>]?]>]?!?]?!?]?!@!?]@!?!@[@!@\n"
						+ "==^<_<_=_=^=^=_=^=^>]=^>^>]>^?]>]>]?]?]?]?!?]?!@!?]@!@!@!@[@!@!A\n"
						+ "==_<_<_=^<_=_=^=^>^=^>^>]>^>]?^>]?]?]?!?]@!?]@!?!@!@!A!@!@[A!A[A\n"
						+ "==_;_=_<_=_<_=^>_=^>^>]=^?^>]?]>]?]?]@]?!@]?!@!@!A!@!A[@!A[A[A!B\n"
						+ "==_;`<_=_<_=_=_=^>_=^>^>]>^?]?^?]?]?!@]@!?]A!@!A[@!A[A!B[A[B[B[B\n"
						+ "==`;_<`<_=`=_=^=_=^>_>^?]>^?]?]?]@]?]@!@]A!@!A[A!A[B!A[B[BZC[B[C\n"
						+ "==`;`<`<_=`=_=_=_>^>^>^>^?^?]?]@]@]@!@]A!@!A[B!A[B!B[B[BZC[CZCZC\n"
						+ "==`;`<`<`=`=_=_=_>^>^?_>]?^?]@]@]@]@!A]A!A[A!B[B!B[C[BZC[CZCZDZC\n"
						+ "==`;a;`<a=_<`=_>_=_?^>^?^?]?^@]@]@!A!A]A[A!B[B!C[BZC[CZDZCZDZDZD\n"
						+ "==a:a;a<`<`<`=_>_>_>^>^?^@]@]@]@]A!A!A!B[B!B[B[C[CZC[DZCZDZDZDZD\n"
						+ "==a:b;a<`<`<`=_>_=_?^>^@^?^@]@!A]A!A!B!B[B[C!CZC[CZD[DZDYDZDZEYE\n"
						+ "==b:a;a;a<`=`=`=_>_>^?^@^?]A]@]A!A!B!B!B[C[C[CZD[DZDZDZEYDZEYEZF\n"
						+ "==b:a;b;a<`=`=`=_>_?^?^@^?]A]A]A!A!B!C[B!CZC[D[DZDZEZDYEZEYFYEYF\n"
						+ "==b9b;b;a<a<`=`>_>_?_?^?]@^A]A!A]B[C!B[D[C[DZDZEZDZEYFYFYFYFYFXG\n"
						+ "==b9c;b;a<a<`=`>`>_>^@^@^@]A]A]B!B[C!C[C[DZDZEZEZEYFYFYFYGXFYGXG\n"
						+ "==c9c:b;a<b<`=`=`?_>^@^@^@]A]B]B!B[C!DZD[DZEZEZFYEYGYFXGXGXHXHXH\n"
						+ "==c9c:c;b;a<a=`>`>_?^@_@]A]A]B!B!C!D[DZD[EYFZEYGYFYGXHXGXHWIWHWI\n"
						+ "==d8d:c:b;b<a=`=`?_?_@^@]A]B]B!C[D[D[EZEZFZFYFXHYGXHWHXIWIWIWJVI\n"
						+ "==d8d9d:b;b<a=a>`>_@^@^@^B]B!C!C[D[EZEZFZFYGYGXHXHXHWIWIWJVJWJVK\n"
						+ "==e8d9c:c;b<b=`>`>_@_@^A^A!C]C[D!DZEZFZFYGYHXHXHXIWJVIWKVKVKUKUL\n"
						+ "==e7e9c;c:c<a=a>`?_?_A^A]B]C!C[D[E[FYFZGYHXHXIWIWJWJVKVKULVLTLUM\n"
						+ "==e7e9d:c;c<a=a>`?`?^A^A]C]C!D[D[FZFYGYGYIXIWIWJVKVKVLULUMTMTNTN\n"
						+ "==f7e9d:c;c;a=a>`?`@^A^B]B]D[D!EZFZFYHYHXIWIWJWKVLULULUMTNSNTOSO\n"
						+ "==e7e9e:c;c<a=a>`?`@^A^B]C]C!E[EZFZGYHXHXJWJWJULVLUMTNTNSOSORPRQ\n"
						+ "==e7f:d:c;c<a=a>`?`A^A^B]C!D!D[FZFYHYHXIXJWJVLULUMUMTOSOROSQQQQR\n"
						+ "==e7e8e:c:c<b<a=`>`?_@^A^B]B!D[D[EZFYGYGXIXIVJWKUKUMTMTNSNSORPQQ\n"
						+ "==f7e:d:d;b<b>a>`?_A^A^C]C!D[F[FYGYHYIWJWKVKVMTMUNSOSPRPQQQRQSOT\n"
						+ "==f7e9d;d;b<b=a?`?_A^B^B]D[D!FZFYHYHXJWJWKVLUMTNTOSORPRRQRPRPTOT\n");
	};

	public String C_BAPTA() {
		return new String(
				"==]=]=^=]>^=]=]=^>]=^=]>^=]=]>^=]>]=]=^>]=]>]>]=^>]>]=]>]>]>]>!>\n"
						+ "==^<]=^<^=^=]=^=]>^=]=^>]=]=]>]>^=]>]>]=]>]>]>]>]>]=!>]>]>]>]>]=\n"
						+ "==]<^=]=^=^=]=]=^=]>^=]=]=^>]=]=]>^=]>]=]>]=]>]=]>]>^=]>]>!=]>]>\n"
						+ "==]<^=]=^>]=^=]=^=]=]>^=]=]>]=^>]=]=]>]=^>]>]=]>]=]>]>]=]>]=]>]>\n"
						+ "==]<^=^=]=^=]>^=]=^=]>]=^=]>]=]>^=]>]=]>]=]>]>]=]>]>]>]=]>]>]>]>\n"
						+ "==]=]=^=]=^=]=]>^=]=]=^>]=]=]>^=]>]=]=]>^=]>]=]>]=]>^=]=]>]=]>]=\n"
						+ "==]=]=^=]=^=]>]=^=]>]=]=^>]=]>]=^=]>]=]>]=]>^=]>]=]>]=^>]=]>]=]=\n"
						+ "==]<^=]=^=]=^>]=]=^=]=]>^=]=]>]=]=^>]=]>]=]>]=]>^=]>]=]>]=]>]=]>\n"
						+ "==]<^=]>^=]=]=^=]=]>^=]>]=]=^>]=]>]=]>]=^>]=]>]=]>]=]>^=]>]=]>]=\n"
						+ "==^<^<]=^=^<^=]=^=^=]=]=^=]>]=^=]>]=]>]>]=]>]>]=]>]>]>!>]>]>]>!>\n"
						+ "==]<^=^=^=]<^=^=^=]<^=^=]=^=]=^=]>^=]=]>]=^>]=!>]>]>]>!?]>!>!?!?\n"
						+ "==]<^=^=^=^<^=]=^=^=^=]=^=^=]>^=]=]>^=]>]>]=]>]>]>!?]>!?]>!?!?!?\n"
						+ "==^;_<_<_<^<_<^<^=_<^=^<]=^=^>]=^>]=]>]>]>]?!>!?]?!?!@[?!@[@[@[A\n"
						+ "==_:`<_;_;_<_<_;_<^=_<^=^=^=^=]=^>]>]>]>!?]?!?!@!?[@[A[@[A[BZAZB\n"
						+ "==_:`;`<_;_<_;_<_=_<^<_=^=^>]=^>]>]>]?]>!?]@!?[@!@[@!AZA[BZAZBZB\n"
						+ "==`:a:`;`;`;_<`<_<_<_=^<^>^=^>^>]>]?]?!?!@!@!A[@[A[BZB[BYCZCYCYD\n"
						+ "==`:a:a:`;`;`<`<_<_<_=^=_=^>^=]?]>]@]?!@!@!@[A[A[B[BZBZCYDYCYDXE\n"
						+ "==a9a;a:`;`;`<`<`<_=_=^=_>^=]?^?]?]?!@!@!A!A[A[B[BZCZCYDYDYDYEXE\n"
						+ "==a:`;a;a;`<`;`<`<`=_=_=_=^>^?^>]?^@!@]@!A[B[B[BZCZDYEXEYEWFWGWH\n"
						+ "==`;a<`<`<a<`<`<`=_=`=_>_=^?^>^?^?]@]A!@!B!B[B[CZDYDYEYFXFWHVHVH\n"
						+ "==a:b:a;a<a;a<`<`=`=_=_>_>^?^?^?]@]A!A!A[C[B[DZDYDYEYFXGWGWGVIVI\n"
						+ "==b9b;b:a;b<`;a=`<`>_=_>_?^?^@]@]@]B!A[C[C[CZDYEYFYFXFWHWHWIUIVJ\n"
						+ "==b9b;b;b;a;a<a=`=`=_>_>_?^?^@]A]A]A[C!B[DZDZEYEYGXFXHWHVIVJUKTK\n"
						+ "==b:c:b;b<a<a<a=`>`>_>_?^@^@^@]B]A!C!C[CZEZEZEYGXGXHWHWIVJUKULTL\n"
						+ "==c9c;b;b;b<a<a=`=`>_?_?_@^@]A]B]B!C[D[DZEYFYFYHXHWHVJVJVKTLTMSM\n"
						+ "==c9c:c;b;b;a=a<`>`>_>_?_@^@]A]B!B!C[D[DZEZFYGXGXHWHWJUJVKTLTMTM\n"
						+ "==c:c;b;c<a=b<a>`>`?_?_@_A]A^B!B!D!D[EZFYGYGXHWJWJVKULTMTNSOQPQQ\n"
						+ "==d8e9c;c:b<b<a>a>_>`@^@^A]B]C!C[D[EZFZGYGXHWIWJVJVLULULSNSOSORP\n"
						+ "==d9d:c:c<b<b<a>`>`?_@_A^A]B]C!D[E[EZFYGXHXIXJVJVLULTMTNSORPRPPR\n"
						+ "==d8e:c;c;b<b=a>a>_?_@_A]B^C!C!D[EZFYGYHYHWJWJVLULUMSNSOSOQQQRPR\n"
						+ "==d8e:c;d;b<b=a>`>`@_@^A^B!C]D[E[EZGYGYHWIWJWKULUMTNSORPRQQRPROT\n"
						+ "==d9d:d;b<c<a=a>a?_?_A_A]B]C!D!EZFZFYHYHWJWJVLUMTMTORPRPQROSOUNU\n"
						+ "==e8e9d;c;c<a=a>a?_A_A]A]C]D[E[FZGYHXHXJVKVLVLTNSOSORQQQPSPTOTNV\n"
						+ "==e8e9e:c;c<a>a>`?_A_A]B]D!D[FZFYGYIXJWJVLULTNTOSOQQRRPSOSOUMVMW\n"
						+ "==e8d:d;c<b<b=a?`?_@^B^B]C!D[FZFZGXIXIWKVKUMTNSOSPQQQSOTOTMWMWKX\n"
						+ "==e8e9d;c;b=a=a?`?_A^A]C]D[D[FZGXHXJWJVKVMTNSORPRQPSOTOUMVMWKYJZ\n"
						+ "==c;c;b<b=b=a>`?`?_A_A]B]C]D[E[FYGYHXIVKVLUMSNSPQQPSOUNULXKYJ[H!\n"
						+ "==d9d:c;c<a=b=`>`@_@^A^B!C!E[EZGYGXIWJVKUMSNSORQQROSNUMVLXJZJ[H!\n"
						+ "==e8d9d:c;b<b=a>`?_@^@^B!C!D[EZFYGXIXJVKULTMSORPQRPRNTNVLWKYJZI[\n");
	};

	public String C_A231817() {
		return new String(
				"==]=^=^>^=^=]>^>^=]>^>]>^=]>]>^>]>]>]>]?]>]>]?]>]>]?]>!?]?]>!?]?\n"
						+ "==]=^=^>^=^>]=^>^=]>^>]>^>]=]>^>]>]?]>]>]>]?]>]>]?]>]?!>]?]?!?]>\n"
						+ "==^=]=^=^>^=^>^=]>^>]=^>]>^>]>]>^>]>]>]>]?]>]?]>]>!?]?]>!?]?!?]?\n"
						+ "==]=^=^>^=^>]=^>^=]>^>]>^=]>]>^>]>]>]?]>]>]>]?]>]?]>]?!>]?]?!>]?\n"
						+ "==]=^=^=^>^=^>]=^>^>]>^=]>]>^>]>]>^>]>]?]>]>]?]>!?]>]?]>!?]?!?]?\n"
						+ "==^=^=]=^>^=^>^=]>^>]>^=]>^>]>]>^>]>]?]>]>]?]>]?]>!?]>]?!?]?!?]?\n"
						+ "==^=^=^=]>^=^>^=]>^>^=]>^>]>]>^>]>]>]?]>]>]?]>]?]>!?]?]>!?]?!?]?\n"
						+ "==^=^=^=^=^=^>^=^>^=^>]>^>]>^>]>]>]>]?]>]?]>]?]?!?]?!?]?!?!?!?]@\n"
						+ "==^<_=_=^=_=^=^>^=^>^=^>]>^>]>^>]?]>]?]?!>]?]?!@]?!?!@!@!?!@[@!@\n"
						+ "==_<_<_=_=_<^=_>^=^=^>^>^>^>]>]?]>^?!?]?]?!?]@!?!@!@!@[@!A[@[A[A\n"
						+ "==_<`<_<_=_<_=_=^>_=^>^=^>]?^>]?]>]?]?]?!@]@!?!@!@[A!@[A[A[A[A[B\n"
						+ "==`;`<`<_<`=_=_=_=^=_>^>^>^>]?]>^?]@!?]@!?!A!@!@[A[A[A[B[AZBZBZC\n"
						+ "==`;a<`;`=`<_=_=_=_=_>^>^>^?^>]?]@]?!@]@!@!A[A!A[A[BZB[BZBZCZCYC\n"
						+ "==a;a;`<a<`<`=_<_>_=_>^>_>]?^?]?]@]@]@!@!A[A!B[A[BZBZCZCZCYCZDXD\n"
						+ "==a:a<a;a<`=`<`=`>_=_>^?^>^?^?]@]@]@!A!A!A[B[A[C[BZCZCYDYDYDYEXE\n"
						+ "==b:a;b<a<`<a=`=_=`>_>^?^?^?^?]@]A]@!B!A[B[B[CZB[DYCZEYDYEXEXEXF\n"
						+ "==b:b;b<a<a<`=a=_=`>_?_>^?^@]@^@!A]A!B[B!B[CZCZCZDYEZEXEXEXFXGWF\n"
						+ "==b:c;b<a<b<`=a=`>_>_>_?^@^@^@]A]A!A!C[B[C[CZDZDYEYEYFXFXFWGWHWG\n"
						+ "==c:b;c<a<b<a=`>`>`>_?_?^@^@]A]A]B!B!B[C[DZDZDZEYFXEYGWGXGWHVHVH\n");
	};

	public String W_air() {
		return new String(
				"==^<^<^=^=_<^=]=^=^=^=]>^=]>^=]>]>]>^=]?!>]>]>]?!>]?]>!?!?]?!?!?\n"
						+ "==]=^=^>^=]>^=]>^=]>^>]>^>]=]>^>]>]?]>]>]>^>]?]>!>]?]>]?]?]>!?]?\n"
						+ "==^<^=^=^=^=]>^=^=^>]=^>]>^=]>]>^>]>]>]>]?]>]>!?]>]?]?!>]?!?]?!?\n"
						+ "==^<_<_<^<_=^=^<^=^=^>^=]>^=]>^>]>]>]>]?]>!?]?!>]?!@!?!?!@!@!@[@\n"
						+ "==_;^=_<_<^=_=^=^=^=^=^>^=]>^>]>]>]?]>]?]?]?!?!?]?!@!@!?[@!A[@!@\n"
						+ "==_;_;_<_<_<_<^=^=_<^=^=]>^=]>^>]>]>]?]>!?]?!?!?!@!?!@!@[A[@!@[A\n"
						+ "==^<_<_=^=_<^=^=^>^=^=^>]>^>]>]>^>]?]>]?!?]?]?!?]?!@!?!@!@!@[@!@\n"
						+ "==_;`;`;_<_<_<_<^=_=^=^=^>^=]>^>]?]>!?]?]?!@!?!@!@[A[@!A[AZA[B[A\n"
						+ "==_;_<`<_<_<^<_=_=^<^>_=^=]>^>]>^>]>]?]?!?]@!?!@!@[@!A[A[A[AZB[B\n"
						+ "==_;_<`<_<_=_<^=_=^>^=^>^=]>^?]>]?]?]?]?!?]@!@!@!@!@[A!A[A[A[A[B\n"
						+ "==`;`;`<_<`=_<_=^=_=^>^>^>^>^?]?]?]@]?!@!@!A!@!A[B!A[BZB[BZBZCZC\n"
						+ "==`;`;`<`<_<`=_=_=^=_>^>^>]?^>]?]@]?]@!@!A!@!A!A[B[B[BZB[CZCZCZC\n"
						+ "==`;a;`;`<`=`<_=_>_=_>^>^?^?]?^?]@!@]A!@!A!B[B[B[B[CZCZCZDZDYDYE\n"
						+ "==a:a<`<a<`=_=`=_>^?_>^?]@^?]@]A]A!A!A!B!B[B[B[C[C[DZCZDZDZEZDYE\n"
						+ "==a;a;`=a<`=_=`=_>_?^>_?^?]@^@]A]@!A!B!B!B[B[C[C[DZDZDZDYEZEYFXF\n"
						+ "==a;a<a=`<`>`>_>`>^?_@^?]A^@]A]A!B]B!B[C!C[C[D[DZDZEZEZEYFYEYGYF\n"
						+ "==`<a<a=`=`>_=`>_?_?^?^?_@]@^A]@]B!A]B!B[C!C[C[DZDZEZEZEYFYFXFXG\n"
						+ "==a;a<a<a=a=`=_>`>_?_?^?^@^@]A^A!B]B!B[C!C[DZD[EZEYEYFYFXGXHXHWH\n"
						+ "==b:b<b;a=a=`=`>`>_?_@^@^@^A]A!B]B!C[D!CZE[DZEZFYFYGXGYGWHXIWHWJ\n"
						+ "==b;b;a<a=a=`>`>_>_?_@^@^@]A^A!B]B!C!C[D[D[DZEZEZFYFYGXGXGXHXHWI\n"
						+ "==c:b;b<b=a=`>`>_?_?_A^@^A]B]B]C!C[D!D[DZEZFZFYGYGYGXHXHWIWJWJVJ\n"
						+ "==b:c;b;a<b=`=a=`?_>_?_@^@^A]B]A!C!C[D[D[EZEYFZFXGXHXHWIWIVJVKVK\n"
						+ "==b:c;b<b<a<a>`=`?_?_?_A^@]B]A]C!C[D!DZD[FYFZFYGXGXHXHXIWIVJWJVJ\n"
						+ "==b;b;b<b=a=`>`>`?_?_@^@^A^A]B!C]C[C!D[EZEZFZFYFYHXGXIXHWIWJVJVK\n"
						+ "==b:c<b<a<a=a>`>`?_?_@^@^A]A]B]C!C!C[E[D[EZFYFYGYGXHXHXIWJWIVKVK\n"
						+ "==b;c;b=a<a>a>`?_?`@^@^A^A]B]C]C!C[E[D[FZEZGZFYHXGXIXIXIWJVJVKVK\n"
						+ "==c:c;b;b=a<a>`>`>_@_@^@^A^B]B!C!C!D[DZEZFZFYGYGXHXHXIWJWJVJVKUL\n"
						+ "==c:c;b;b<b=`>a>_?`?^@_A]A^B]C!C!D[D[EZEZFZGYGYGXHXIWIWJWJVJVKUL\n"
						+ "==c:c<b<b=a=a>`?`?_@^@_A]B^B!C]D[D!EZE[FZFYGYHYHXHWJXIVKWKUKVLUL\n"
						+ "==c:c;c;b=b<`>a>`?_@_@^A^B]B]C!C!D[E[EZFZGYGYHXHWIXJVJWKUKVLTMUM\n"
						+ "==c;b<b=b>a>`>`@`@_@_A^B]B^C!D!D!D[F[FZFZGYHYHXIXJWJWKVKVLUMUMUM\n"
						+ "==d9c;c;c<a=b=`?`?`?^A_A]B]B]C!D[E[EZFZGYGYHXIXIWJVKWKULULUNTMSO\n");
	};

}

gpl-3.0.txt Listing
--------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>  Everyone is permitted to copy and distribute verbatim copies  of this license document, but changing it is not allowed.

Preamble

  The GNU General Public License is a free, copyleft license for software and other kinds of works.

  The licenses for most software and other practical works are designed to take away your freedom to share and change the works.  By contrast, the GNU General Public License is intended to guarantee your freedom toshare and change all versions of a program--to make sure it remains free software for all its users.  We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors.  You can apply it to your programs, too.

  When we speak of free software, we are referring to freedom, not price.  Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights.  Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received.  You must make sure that they, too, receive or can get the source code.  And you must show them these terms so they know their rights.

  Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software.  For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.

  Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so.  This is fundamentally incompatible with the aim of protecting users' freedom to change the software.  The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable.  Therefore, we have designed this version of the GPL to prohibit the practice for those products.  If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary.  To prevent this, the GPL assures that patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and modification follow.

TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this License.  Each licensee is addressed as "you".  "Licensees" and "recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy.  The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based on the Program.

  To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy.  Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other parties to make or receive copies.  Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License.  If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work for making modifications to it.  "Object code" means any non-source form of a work.

  A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form.  A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities.  However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work.  For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

  The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

  The Corresponding Source for a work in source code form is that same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met.  This License explicitly affirms your unlimited permission to run the unmodified Program.  The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work.  This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force.  You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright.  Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under the conditions stated below.  Sublicensing is not allowed; section 10 makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

  When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7.  This requirement modifies the requirement in section 4 to "keep intact all notices".

    c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy.  This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged.  This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

  A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit.  Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

    a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.

    d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge.  You need not require recipients to copy the Corresponding Source along with the object code.  If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source.  Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling.  In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage.  For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product.  A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source.  The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

  If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information.  But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

  The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed.  Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law.  If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it.  (Additional permissions may be written to require their own removal in certain cases when you modify the work.)  You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or authors of the material; or

    e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

  All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10.  If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term.  If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly provided under this License.  Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

  However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

  Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License.  If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or run a copy of the Program.  Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance.  However, nothing other than this License grants you permission to propagate or modify any covered work.  These actions infringe copyright if you do not accept this License.  Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License.  You are not responsible for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations.  If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License.  For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based.  The work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version.  For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement).  To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

  If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients.  "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

  A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License.  You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License.  If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all.  For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work.  The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time.  Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

  Each version is given a distinguishing version number.  If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation.  If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

  Later license versions may give you additional or different permissions.  However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

END OF TERMS AND CONDITIONS

How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>  Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.
--------------------------------------------------------------

  If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License.  Of course, your program's commands might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program into proprietary programs.  If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library.  If this is what you want to do, use the GNU Lesser General Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--------------------------------------------------------------

GraviJa.java Listing
--------------------------------------------------------------

//
import java.applet.Applet;
import java.awt.BorderLayout;
import java.util.StringTokenizer;

@SuppressWarnings("serial")
public class GraviJa extends Applet {
	static final boolean UNLOCK_EXPERIMENTAL_FEATURES = false;
	ModelControls controls;
	RightControls rcontrols;
	TopControls tcontrols;
	ModelCanvas c;
	SolvingThread solver;

	public void init() {
		setLayout(new BorderLayout());

		add("Center", c = new LPK(this));
		add("North", tcontrols = new TopControls(this));
		add("East", rcontrols = tcontrols.r);
		add("South", controls = new ModelControls(c));
		c.app = tcontrols.app = rcontrols.app = controls.app = this;
	};

	public void BeginSolution() {
		if (solver != null && solver.isAlive())
			breakSolution();
		else {
			solver = new SolvingThread(this);
			solver.start();
		}
	};

	public void breakSolution() {
		if (solver != null && solver.isAlive())
			solver.stop();
		controls.B_F9.setLabel("Solve F9");
		System.gc();
	};

	public void start() {
		controls.setEnabled(true);
	}

	public void stop() {
		controls.setEnabled(false);
	};

	/**
	 * Returning true would unlock experimental features.
	 */
	boolean mine() {
		return UNLOCK_EXPERIMENTAL_FEATURES;
	};

	public double toDouble(String text) {
		return Double.parseDouble(new StringTokenizer(text).nextToken());
	};
}

LayoutHelpers.java Listing
--------------------------------------------------------------

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Panel;

/**
 * Various tiny classes that no one would define in these days.
 */

@SuppressWarnings("serial")
class MyPanel extends Panel {
	GraviJa app;

	public MyPanel() {
	};
};

@SuppressWarnings("serial")
class DiEntry extends Panel {
	public DiEntry(Component C1, Component C2) {
		setLayout(new GridLayout(2, 1));
		add(C1);
		add(C2);
	};
};

@SuppressWarnings("serial")
class TriEntry extends Panel {
	public TriEntry(Component C1, Component C2, Component C3) {
		setLayout(new BorderLayout());
		add("East", C1);
		add("Center", C2);
		add("West", C3);
	};
};

@SuppressWarnings("serial")
class QuadEntry extends Panel {
	public QuadEntry(Component C1, Component C2, Component C3, Component Cp) {
		setLayout(new BorderLayout());
		Panel p = new Panel();
		p.setLayout(new GridBagLayout());
		p.add(C3);
		p.add(Cp);
		add("East", p);
		add("Center", C2);
		add("West", C1);
	};
};

LPK.java Listing
--------------------------------------------------------------

/**
 * This is the main mathematical model that was highly successfull. It was
 * presented in the published article and is actually the model used in the applet.
 */
@SuppressWarnings("serial")
class LPK extends TModel {

	public LPK(GraviJa _app) {
		super(_app);
	};

	double Lh; // Local angle bending speed under that
	// dCv reverses sign
	double Cs; // Correction slope.
	double Sde; // Sensitivity decay moving toward the base from the tip.
	double Kw; // Signal decay
	double Au; // Straightening constant
	double Aude; // Straightening decrement

	double Bs; // Initial boost
	double Bde; // Boost decrement in time
	double BSde; // Boost decrement in basip. dir.

	double Tr_Off; // Angle under that transmission is blocked
	boolean Apex_Pinned = false; // 0 = base pinned; 1 = apex pinned.
	double _g; // Old value when switching off
	double Gravity_OFF;
	double Gravity_ON;
	double wLa[]; // Ankstesnes iteracijos lokalus kampas.
	double G[]; // Gravitropinio sudirginimo signalas.
	double Stra[]; // Signalas, atsizvelgiant is susilpnejima. Rodymui.
	boolean Lab[];

	public void beginSolution() {
		super.beginSolution();
		wLa = new double[segs + 1];
		G = new double[segs + 1];
		Stra = new double[segs + 1];
		Lab = new boolean[segs + 1];

		Sde = Arg("Sde", 0.0);
		Au = Arg("Au", 0.0);
		Aude = Arg("Aude", 0.0);
		// Apex_Pinned=Arg("Apex_Pinned",0)>0.5?true:false;
		Tr_Off = Arg("Tr_Off", 0.0);
		Bs = Arg("Bs", 0.0);
		Bde = Arg("Bde", 0.0);
		BSde = Arg("BSde", 0.0);
		// Klinostatas:
		// g=Arg("g",1.0);
		_g = g;
		Gravity_OFF = Arg("Gravity_OFF", 99999.0);
		Gravity_ON = Arg("Gravity_ON", 99999.0);
		// Hipotezes:
		ConfigBank kon = app.rcontrols.d.Config;
		if (!kon.Sh_Mod.getState())
			Break_Program = true;
		if (!kon.Hyp_Apical.getState())
			Ks = 0;
		if (!kon.Hyp_SigDec.getState())
			Sde = 0;
		if (!kon.Hyp_Local.getState())
			Bs = 0;
		if (!kon.Hyp_Auto.getState())
			Au = 0;
		
		charts = kon.Sh_Tip.getState();

		if (!charts)
			dc.clearRect(0, 0, MaxX, maxY);
		if (XData = kon.Sh_Exp.getState())
			experiment();
	};

	double Local_perception(double alpha, double s) {
		return Bs * g * exp(-Bde * Math.abs(alpha)) * exp(-BSde * s);
	};

	double Perception(double alpha) { // app.Conclude(String.valueOf(alpha));
		return g * Ks * cosd(alpha);
	};

	void DoSolution() {
		int k;
		double v0; // Linkimo greitis be korekcijos
		double v; // Koreguotas linkimo greitis
		double s; // Atstumas nuo virsunes vieneto dalimis (0=tip, 1=base);
		if (!Break_Program)
			do {
				if (!Apex_Pinned) { // Base - pinned:
					{
						G[segs] = Perception(La[segs]); // Percepcija
						for (k = 0; k <= segs; k++) {
							s = ((0.0 + segs - k) / segs);
							Lab[k] = (abs(La[k] - 90.0) < Tr_Off) ? true
									: false;
							if (!Lab[k]) {
								if (k > 0) {
									G[k - 1] = G[k];
								}
								;
							}
							;
							// (signalas peresamas jei nuo vertikales nukrypta
							// daugiau 10 deg)
							v0 = Stra[k] = G[k] * exp(-Sde * s);
							// Autoissitiesinimas:
							v = v0 - Au * Cv[k] * exp(-Aude * s);
							;
							v = v + Local_perception(La[k], s);
							dCv[k] = v;
						}
						;
					}
				}
				;
				if (Apex_Pinned) { // Apex - pinned:
					{
						G[0] = Perception(La[0]); // Percepcija
						for (k = 0; k <= segs; k++) {
							s = ((0.0 + segs - k) / segs);
							s = 1.0 - s;
							v0 = Stra[k] = G[k] * exp(-Sde * s);
							// Autoissitiesinimas:
							v = v0 - Au * Cv[k] * exp(-Aude * s);
							v = v + Local_perception(La[k], s);
							dCv[k] = v;
						}
						;
						// Reikalingas atskiras signalo pratraukimo ciklas:
						for (k = segs - 1; k >= 0; k--) {
							Lab[k] = (abs(La[k] - 90.0) < Tr_Off) ? true
									: false;
							if (!Lab[k]) {
								G[k + 1] = G[k];
							}
							;
						}
						;
					}
				}
				defaultRun();
			} while (!Break_Program);
	};

	void defaultRun() {
		super.defaultRun();
		if (time > Gravity_OFF) {
			g = 0.0;
		}
		if (time > Gravity_ON) {
			g = _g;
		}
		;
	};

	void finishSolution() {
		wLa = G = Stra = null;
		Lab = null;
		super.finishSolution();
	};
}

Main.java Listing
--------------------------------------------------------------

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Main {

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {
				JFrame frame = new JFrame("Compensation demonstrator");
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				frame.setSize(780, 640);
				GraviJa applet = new GraviJa();
				applet.init();
				applet.start();
				frame.add(applet);
				frame.setVisible(true);

				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						applet.controls.resolve();
					}
				});
			}
		});
	}
}

model parameters.txt Listing
--------------------------------------------------------------

"Model parameters: \r\n"+
"Ks\t1.00\tSensitivity constant\r\n"+
"Au\t0.0250\tAutotropism \r\n"+
"Aude\t8.20\tAutotropism decrements\r\n"+
"Sde\t1.57\tCompetence function decay.\r\n"+
"Kr\t200 \tRealization constant\r\n"+
"Tr_Off\t0.0\tAngle from vertical below that transmission is blocked, \r\n"+
"\r\n"+
"Local perception:\r\n"+
"BSde\t4\tDecremet in basip. dir\r\n"+
"Bde\t0.07  \tDecrement from angle\r\n"+
"Bs\t2.4\tLocal perc. const.\r\n"+
"\r\n"+
"Stem position:\r\n"+
"Gravity_OFF\t0.1 0.75\r\n"+
"Gravity_ON\t999 2.75\r\n"+
"\r\n"+
"g\t1\r\n"+
"Apex_Pinned\t0 ( 0 for base-, 1 for apex- pined stem)\r\n"+
"\r\n"+
"Solver parameters:\r\n"+
"Segs\t87\t  \t(number of segments)\r\n"+
"dT\t0.04166666\t(step in time)\r\n"

ModelCanvas.java Listing
--------------------------------------------------------------

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Event;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.text.DecimalFormat;

/**
 * Canvas of the main modelling area.
 */
@SuppressWarnings("serial")
class ModelCanvas extends Canvas {
	public double g = 1.0;
	// ************* Model parameters ***********
	double Kw, K, beta, t_, G;
	// ******************************************
	double startAngle = 0;
	double finalTime = 6;

	double finalAngle = 360;
	double initialAngle = -360;
	double maxAngleChange = 1;
	// ******************************************

	boolean filled = false;
	double zoom = 1.0;
	
	public boolean charts = true;

	int nSteps = 100; // Zingsnius skaicius
	double r_T[] = new double[nSteps + 1]; // laikai
	double r_Y[] = new double[nSteps + 1]; // Y'ai

	double d_T[] = new double[100]; // Duomenu laikai
	double d_Y[] = new double[100]; // Duomenu Y'ai
	double dE[]; // Duomenu standartines vidurkio paklaidos

	int dS = 0; // Duomenu kiekis

	int XMA = 120;
	boolean IntKey = false;
	FontMetrics metrics;
	GraviJa app;
	int vx, vy, hx, hy;

	public ModelCanvas(GraviJa _app) {
		app = _app;
		setBackground(Color.white);
		for (int k = 0; k <= nSteps; k++)
			r_T[k] = r_Y[k] = 17.0;
	};

	// Moralinis grafikos palaikymas:
	double smx = 1, smy = 1, siay = 0;
	int sdx = 0, sdy = 0, sry = XMA;

	int gx(double x) {
		return (int) Math.round(smx * x) + sdx;
	};

	int gy(double y) {
		return sry - (int) Math.round(smy * (y - siay)) + sdy;
	};

	int SW(String s) {
		return metrics.stringWidth(s);
	};

	void print(String s) {
		/*
		if (app != null)
			app.showStatus(s);
			*/
	};

	public boolean keyDown(Event e, int k) {
		if (k == Event.F9) {
			app.controls.resolve();
			return true;
		}
		;
		/*
		 * if (k==Event.F3) { Force_Kampai=true; app.controls.resolve(); return
		 * true; };
		 */

		return false;
	};

	public boolean mouseMove(Event e, int x, int y) {
		Double xx = new Double((x - sdx) / smx);
		Double yy = new Double((-y + sry + smy * siay + sdy) / smy);
		if (charts)
			print("The mouse is pointing at time " + xx.toString() + " angle "
					+ yy.toString());
		return true;
	};

	public boolean mouseExit(Event e, int x, int y) {
		print("");
		return true;
	};

	boolean space(String s, int i) {
		return Character.isWhitespace(s.charAt(i));
	};

	private String _d_buf = "";
	double _d_time;
	double _d_dat;
	double _d_se;
	int _d_I;
	int _d_L;

	boolean _nextData() {
		int K = _d_I;
		int D;
		String s;
		D = K;
		if (D >= _d_L)
			return false;
		if (_d_buf.charAt(K) == '(')
			return false;

		while (!space(D)) {
			D++;
			if (D > _d_L)
				return false;
		}
		s = _d_buf.substring(K, D + 1);
		_d_time = Double.valueOf(s).doubleValue();
		while ((D < _d_L) && space(D)) {
			if (D > _d_L)
				return false;
			D++;
		}
		K = D;

		while (!space(D)) {
			D++;
			if (D > _d_L)
				return false;
		}
		s = _d_buf.substring(K, D + 1);
		_d_dat = Double.valueOf(s).doubleValue();
		while ((D < _d_L) && space(D)) {
			D++;
			if (D > _d_L)
				return false;
		}
		K = D;

		while (!space(D)) {
			if (D > _d_L)
				return false;
			D++;
		}
		s = _d_buf.substring(K, D + 1);
		_d_se = Double.valueOf(s).doubleValue();
		while ((D < _d_L) && space(D)) {
			D++;
			if (D > _d_L)
				return false;
		}

		K = D;
		_d_I = D;
		return true;
	}

	private boolean space(int pos) {
		return Character.isWhitespace(_d_buf.charAt(pos));
	};

	public boolean getData() // dS - duomenu skaicius skaiciuojnt NUO NULIO
	{
		int k;
		dS = 0;
		_d_buf = app.rcontrols.d.getText() + " ";
		_d_buf.trim();
		_d_I = 0;
		_d_L = _d_buf.length() - 1;
		if (_d_L <= 0) {
			dS = 0;
			return false;
		}
		for (k = 0; k <= 100; k++) {
			if (!_nextData())
				break;
			d_T[k] = _d_time;
			d_Y[k] = _d_dat;
			dE[k] = _d_se;
		}
		dS = k - 1;
		return (dS != 0);
	};

	public double getArg(String arg, String Default) // Paraunam argumenta
	{
		RightControls Q = app.rcontrols;
		String s = " " + Q.p.getText(); // Paraunam argumentu teksta
		Double r = new Double(55.17);
		arg.trim();
		int k, l;
		int i = 0;
		if (s.length() <= arg.length() + 3)
			i = -1;
		else {
			l = arg.length();
			while (i < s.length() - l - 1) {
				i = s.indexOf(arg, i);
				if (i < 0)
					break;
				if (space(s, i - 1) && space(s, i + l))
					break;
				i += l;
			}
		}
		if (i < 0) {
			if (!arg.startsWith("_"))
				Q.p.appendText('\n' + arg + '\t' + Default); // !!!!!!!!!!!
			r = Double.valueOf(Default);
		} else {
			// s = (s.substring(i+arg.length()));
			l = s.length();
			i += arg.length();

			for (k = i; k < l; k++) {
				if (!Character.isWhitespace(s.charAt(k)))
					break;
			}
			i = k;
			for (k = i; k < l; k++) {
				if (Character.isWhitespace(s.charAt(k)))
					break;
			}
			s = s.substring(i, k);
			r = Double.valueOf(s);

		}
		return r.doubleValue();
	};

	double sqr(double x) {
		return x * x;
	};

	// Velinimo sistema:
	int vrange = 4 * nSteps;
	double vY[] = new double[vrange + 1]; // velinimo buferis, parametras
	double vT[] = new double[vrange + 1]; // velinimo buferis, laikas
	int spos, rpos; // rasymo ir skaitymo pozicijos

	void InitDelay() {
		vrange = 4 * nSteps;
		vY = new double[vrange + 1]; // velinimo buferis, parametras
		vT = new double[vrange + 1]; // vezlinimo buferis, laikas

		for (int k = 0; k <= vrange; k++)
			vY[k] = vT[k] = 0;
		spos = rpos = 0;
		t_ = getArg("T_delay", "0.33");
	};

	void Remember(double t, double y) {
		vY[spos] = y;
		vT[spos++] = t;
	};

	/* ner prasmes saugotis geriau ilgas buferis */

	double Delayed(double t) {
		if (t < 0)
			return 0;
		while ((rpos < spos) && (vT[rpos] < t))
			rpos++;
		if (rpos == spos)
			return 0;
		else
			return vY[rpos];
	};

	public void redraw(boolean filled, double start_a, double end_t) {
		/*
		 * Aha, turi procedura kur perduodami visi nupaisymui reikalingi
		 * parametrai
		 */
		this.filled = filled;
		this.finalTime = end_t;
		this.startAngle = start_a;
		app.BeginSolution(); // Solve();
	};

	public void paintCharts(Graphics g) {
		if (finalTime < 0)
			redraw(filled, startAngle, finalTime); // Pirma incializacija -
		// apsieisim
		
		int k; // Ciklams
		metrics = getFontMetrics(getFont());
		int sLH = metrics.getHeight();
		int LH = sLH * 3 / 2;
		int fX = LH / 4;

		findYIntervals(); 

		long a = Math.round((initialAngle)) - 10;
		long b = Math.round((finalAngle)) + 10;
		long c = (b - a) / 2;
		long ak = c > 30 ? 30 : 3;
		long v = (b + a) / 2;
		v = (v / ak) * ak;
		c = (c / ak) * ak;

		c += ak;

		a = v - c;
		b = v + c;

		initialAngle = a;
		finalAngle = b;

		/****** Drawing axes ***********/

		/* butu idomu zinoti is kur bound() zino kieno ribas rast. Faktas, zino */
		Rectangle r = getBounds();
		g.setColor(Color.black);
		vx = fX + SW("000000");
		vy = fX;
		hx = r.width - fX;
		hy = r.height - 2 * fX - 2 * sLH;
		g.drawLine(vx, vy, vx, hy);
		g.drawLine(vx, hy, hx, hy);
		int y = hy, x = vx;

		smx = 0.95 * (hx - vx) / finalTime;
		sdx = vx;
		
		DecimalFormat format = new DecimalFormat();
		format.setMinimumFractionDigits(1);
		format.setMaximumFractionDigits(1);
		
		for (k = 0; k <= 5; k++) // Horizontali asis
		{
			double vT = k * (finalTime / 5);
			x = gx(vT);
			g.drawLine(x, y - fX, x, y + fX);
			g.drawString(format.format(vT), x, y + LH);
		}
		;
		g.drawString("Time", (x - vx) / 2, hy + 2 * sLH + fX);
		x = vx;

		double rAngle = finalAngle - initialAngle;
		smy = 0.95 * (hy - vy) / rAngle;
		sdy = vy;
		sry = hy - fX;
		siay = initialAngle;
		double dd_Y = Math.round(rAngle / 6);
		int ddT = SW("-120 o");
		int ddL = x - SW("o ");
		for (k = 0; k <= 6; k++) // Vertikali asis
		{
			double ly = k * dd_Y + initialAngle;
			y = gy(ly);
			g.drawLine(x - fX, y, x + fX, y);
			g.drawString(Long.toString(Math.round(ly)), x - ddT, y - fX);
			g.drawString("o", ddL, y - 3 * fX);

		}

		/****** Drawing model ***********/

		g.setColor(Color.red.darker());
		int xx = gx(r_T[0]), yy = gy(r_Y[0]);
		for (k = 1; k <= nSteps; k++) {
			g.drawLine(xx, yy, xx = gx(r_T[k]), yy = gy(r_Y[k]));
		}

		/****** Drawing data ***********/

		if (dataAvailable() && dS > 0) {
			g.setColor(Color.gray);
			for (k = 0; k < dS; k++) {
				int _ly = gy(d_Y[k]), _lx = gx(d_T[k]);
				g.fill3DRect(_lx - fX, _ly - fX, 2 * fX, 2 * fX, true);
			}
		}
	}

	protected boolean dataAvailable() {
		return dS > 0;
	}

	private void findYIntervals() {
		int k;
		finalAngle = initialAngle = r_Y[0];
		for (k = 1; k <= nSteps; k++) {
			if (r_Y[k] > finalAngle)
				finalAngle = r_Y[k];
			if (r_Y[k] < initialAngle)
				initialAngle = r_Y[k];
		}
		for (k = 0; k < dS; k++) {
			if (r_Y[k] > finalAngle)
				finalAngle = d_Y[k];
			if (r_Y[k] < initialAngle)
				initialAngle = d_Y[k];
		}
	};
	
	public final void solve() {
		synchronized (ModelCanvas.class) {
			doSolve();
		}
	}

	protected void doSolve() {
		double H, X, v, w, Y, z;
		double f, a, b, c;
		int tt;
		boolean baigta;

		nSteps = 100;

		for (;;) {
			baigta = true;

			// { Runge Kuto Beisikinis }
			H = finalTime / nSteps; // _H;
			X = 0; // _X0;
			w = startAngle; // _Y0;
			v = 0; // _DYDX0;
			Y = w;
			z = v;

			tt = 0;
			InitDelay();

			r_T[tt] = X;
			r_Y[tt] = Y;
			tt++;

			Kw = getArg("Kw", "50.0");
			K = getArg("K", "535");

			IntKey = false;
			do {
				f = Fx(X, Y, z);
				Remember(X, Perception(Y));
				a = H * f;
				X = X + H / 2;
				Y = w + v * H / 2 + a * H / 8;
				z = v + a / 2;
				Remember(X, Perception(Y));
				f = Fx(X, Y, z);
				b = H * f;
				z = v + b / 2;
				f = Fx(X, Y, z);
				c = H * f;
				X = X + H / 2;
				Remember(X, Perception(Y));
				Y = w + H * v + H * c / 2;
				z = v + c;
				f = Fx(X, Y, z);
				Y = w + H * (v + (a + b + c) / 6);
				w = Y;
				z = v + (a + (b + c) * 2 + H * f) / 6;
				v = z;

				r_T[tt] = X;
				r_Y[tt] = Y;

				if (Math.abs(r_Y[tt] - r_Y[tt - 1]) > maxAngleChange) {
					nSteps *= 2;
					baigta = false;
					r_T = new double[nSteps + 1]; // laikai
					r_Y = new double[nSteps + 1]; // Y'ai
					print("Reducing step...");
					break;
				}
				tt++;

				if (tt % 20 == 0)
					print(Double.toString(100 * tt / nSteps));

			} while (!(/* (X>finalTime) || */IntKey || (tt > nSteps)));

			if (baigta) {
				repaint();
				break;
			}
		}
		print("Steps done: " + Integer.toString(nSteps));
	};

	double Perception(double alpha) {
		return -K * G * Math.sin(alpha * Math.PI / 180.0);
	};

	double Fx(double X, double Y, double Y_)
	// Function of the mathematical model.
	{
		return (Delayed(X - t_)) - Kw * Y_;
	};
}

ModelControls.java Listing
--------------------------------------------------------------

import java.awt.Button;
import java.awt.Checkbox;
import java.awt.Color;
import java.awt.Component;
import java.awt.Event;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Label;
import java.awt.TextField;

/**
 * Main controls. 
 */
@SuppressWarnings("serial")
class ModelControls extends MyPanel {
	TextField a, t, g;
	GridLayout Bag;

	ModelCanvas canvas;

	Button Bp = new Button(" + ");
	Button Bm = new Button(" - ");

	Button Tp = new Button(" + ");
	Button Tm = new Button(" - ");
	Button Ti = new Button(" ++ ");

	Button Gp = new Button(" + ");
	Button Gm = new Button(" - ");

	Button B_F9 = new Button("Solve F9");
	Button B_Reset = new Button("Reset");

	Button Zp = new Button("Enlarge");
	Button Zm = new Button("Reduce");

	public Checkbox qf = new Checkbox("Show experimental data");

	public ModelControls(ModelCanvas c) {
		canvas = c;
		Component ia;
		setLayout(Bag = new GridLayout(1, 0));

		setBackground(Color.lightGray);

		add(new DiEntry(new Label("Initial angle, o"), new TriEntry(Bp,
				a = new TextField(Double.toString(c.startAngle) + " \u00ba", 5), Bm)));

		if (c.app.mine()) {
			add(new DiEntry(ia = new Label("Final time, h"), new QuadEntry(Tm,
					t = new TextField(Double.toString(c.finalTime) + " h", 4),
					Tp, Ti)));
		} else {
			add(new DiEntry(ia = new Label("Final time, h"), new TriEntry(Tp,
					t = new TextField(Double.toString(c.finalTime) + " h", 4),
					Tm)));
		}

		add(new DiEntry(new Label("Gravity vector "), new TriEntry(Gp,
				g = new TextField(Double.toString(c.g) + " g", 4), Gm)));
		// add(new Label(""));
		add(B_F9);
		add(B_Reset);
		// add(new Label(" "));
		add(new DiEntry(Zp, Zm));

	}

	public void resolve() {
		canvas.g = app.toDouble(g.getText());
		canvas.redraw(qf.getState(), app.toDouble(a.getText()), app.toDouble(t
				.getText()));
	};

	double INC(double x, double delta, double scale) {
		x += delta;
		return x = Math.round(x / scale) * scale;
	};

	public boolean action(Event ev, Object arg) {
		if (ev.target instanceof Button) {
			// String label = (String)arg;
			if (ev.target == Gp)
				g.setText(Double.toString(INC(canvas.g, 0.1, 0.0001)) + " g");
			if (ev.target == Gm)
				g.setText(Double.toString(INC(canvas.g, -0.1, 0.0001)) + " g");
			if (ev.target == Bp)
				a.setText(Double.toString(INC(canvas.startAngle, 10, 0.01))
						+ " \u00ba");
			if (ev.target == Bm)
				a.setText(Double.toString(INC(canvas.startAngle, -10, 0.01))
						+ " \u00ba");
			if (ev.target == Tp) {
				t.setText(Double.toString(INC(canvas.finalTime, 0.5, 0.01))
						+ " h");
				if (canvas.finalTime <= 0)
					canvas.finalTime = 6.0;
			}

			if (ev.target == Tm) {
				t.setText(Double.toString(INC(canvas.finalTime, -0.5, 0.01))
						+ " h");
			}

			if (!(app.solver != null && app.solver.isAlive())) {
				if (ev.target == Zp) {
					canvas.zoom *= 1.44;
				}
				if (ev.target == Zm) {
					canvas.zoom /= 1.44;
				}
			}

			if (ev.target == B_F9)
				;
			if (ev.target == B_Reset) {
				g.setText(Double.toString(canvas.G = 1.0) + " g");
				a.setText(Double.toString(canvas.startAngle = 0.0) + " \u00ba");
				t.setText(Double.toString(canvas.finalTime = 6.0) + " h");
				app.rcontrols.p.Reset();
				app.rcontrols.d.Reset();
				app.rcontrols.d.Config.Reset();
			}

			if (ev.target == Ti) {
				t.setText("forever");
				canvas.finalTime = -0.1;
			}

			if (canvas.finalTime <= 0.0001)
				t.setText(Double.toString((canvas.finalTime = 0)) + " ?");

			resolve();
			return true;
		}

		return false;
	};

	public boolean keyDown(Event e, int k) {
		if (k == Event.F9) {
			resolve();
			return true;
		}
		;
		return false;
	};
}

MyButton.java Listing
--------------------------------------------------------------

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;

/**
 * The customized button.
 */
@SuppressWarnings("serial")
class MyButton extends Canvas {
	String Title;
	boolean deep = false;
	TopControls t;
	int dx = 0;

	public Dimension minimumSize() {
		Font fontas = getFont();
		FontMetrics f = getFontMetrics(fontas);
		dx = f.getHeight() / 2;
		return new Dimension(f.stringWidth(Title) + 2 * dx,
				(f.getHeight() + 2 * dx));
	};

	public Dimension preferredSize() {
		return minimumSize();
	};

	public MyButton(TopControls at, String s) {
		Title = s;
		t = at;
	};

	public void paint(Graphics g) {
		Rectangle r = getBounds();
		int hx = r.width, hy = r.height;
		g.setColor(Color.lightGray);
		g.fill3DRect(0, 0, hx, hy, !deep);
		g.setColor(Color.black);
		g.drawString(Title, dx, hy - dx * 3 / 2);
		if (deep) {
			g.drawLine(0, 0, 0, hy);
			g.drawLine(0, 0, hx, 0);
		}
		;
	};

	public boolean mouseDown(Event ev, int x, int y) {
		t.act(Title, this);
		repaint();
		return true;
	};

	void Up() {
		deep = false;
		repaint();
	};

	void Down() {
		deep = true;
		repaint();
	};

}

ParaBank.java Listing
--------------------------------------------------------------

/**
 * Helper class to define the bank of parameters.
 */
@SuppressWarnings("serial")
class ParaBank extends Bank {
	public ParaBank(String name, String data) {
		super(name, data);
	};
}

ParaSetControl.java Listing
--------------------------------------------------------------

/**
 * This class defines and controls the parameter set. 
 */
@SuppressWarnings("serial")
class ParaSetControl extends DataSetControl {
	public ParaSetControl(GraviJa app) {
		super(app);
	}

	public Bank getBank(String name, String data) {
		return new ParaBank(name, data);
	}

	void AddAllData() {
		AddData(
				"Coprinus cinereus stems, air control",
				"Model parameters: \n\n"
						+ "Ks\t1.10\tApical perception\n"
						+ "Au\t0.0250\tStraightening \n"
						+ "Aude\t8.20\tStraightening decrements\n"
						+ "Sde\t1.57\tSignal weakening during transmission.\n"
						+ "Kr\t85 \tRealization constant\n"
						+ "Tr_Off\t0.0\tAngle from vertical below that transmission is blocked, \n"
						+ "\n" + "Local perception:\n"
						+ "BSde\t4\tDecremet in basip. dir\n"
						+ "Bde\t0.07  \tDecrement from angle\n"
						+ "Bs\t2.4\tLocal perc. const.\n" + "\n"
						+ "Solver parameters:\n"
						+ "Segs\t87\t  \t(number of segments)\n"
						+ "dT\t0.04166666\t\n"
						+ "\n\nSimulation of microgravity conditions:\n"
						+ "Gravity_OFF\t999\tTime when microgravity begins\n"
						+ "Gravity_ON\t999\tTime when gravity returns\n");

		AddData(
				"C.cinereus, water treatment",
				"Coprinus cinereus stems after \n"
						+ " 2 hour incubation in water "
						+ " (in vertical position)\n"
						+ "Ks\t1\tApical perception\n"
						+ "Au\t0.0250\tStraightening \n"
						+ "Aude\t4.65\tStraightening decrements\n"
						+ "Sde\t1.57\tSignal weakening during transmission.\n"
						+ "Kr\t61.5 \tRealization constant\n"
						+ "Tr_Off\t0.0\tAngle from vertical below that transmission is blocked, \n"
						+ "\n" + "Local perception:  \n"
						+ "BSde\t4\tDecremet in basip. dir\n"
						+ "Bde\t0.07  \tDecrement from angle\n"
						+ "Bs\t2.4\tLocal perc. const.\n" + "\n"
						+ "Solver parameters:\n"
						+ "Segs\t70\t  (number of segments)\n" + "dT\t0.05\t");

		AddData(
				"C.cinereus, 10 mkm cytochalazin D treatment",
				"Coprinus cinereus stems, treated with\n"
						+ "cytochalazin D, 2 hours, 10 mkm\n\n"
						+ "Ks\t1.00\tApical perception\n"
						+ "Au\t0.0225\tStraightening \n"
						+ "Aude\t4.65\tStraightening decrements\n"
						+ "Sde\t1.575\tSignal weakening during transmission\n"
						+ "Kr\t47.5 \tRealization constant\n"
						+ "Tr_Off\t0\tAngle from vertical below that transmission is blocked, \n"
						+ "Local perception:\n"
						+ "BSde\t4\tBd, Decremet in basip. dir\n"
						+ "Bde\t0.087  \tDecrement from angle\n"
						+ "Bs\t2.4\tLocal perc. const.\n" + "\n"
						+ "Solver parameters:\n"
						+ "Segs\t200\t  (number of segments)\n"
						+ "dT\t0.05\t  \n");

		AddData(
				"C.cinereus, 1 mkm cytochalazin D treatment",
				"Coprinus cinereus stems after \n"
						+ " cytochalazin D treatment (1 mkm, 2 hours)\n"
						+ "Ks\t1.00\tApical perception\n"
						+ "Au\t0.0225\tStraightening \n"
						+ "Aude\t4.65\tStraightening decrements\n"
						+ "Sde\t1.575\tSignal weakening during transmission.\n"
						+ "Kr\t47.5 \tRealization constant\n"
						+ "Tr_Off\t0\tAngle from vertical below that transmission is blocked, \n"
						+ "\n" + "Local perception:\n"
						+ "BSde\t4\tBd, Decremet in basip. dir\n"
						+ "Bde\t0.087  \tDecrement from angle\n"
						+ "Bs\t2.4\tLocal perc. const.\n" + "\n"
						+ "Segs\t70\t  (number of segments)\n\n"
						+ "Solver parameters:\n" + "dT\t0.05  \n");

		AddData(
				"C.cinereus, A231817 treatment",
				"Coprinus cinereus stems, treated with A231817\n"
						+ "(2 hours, 1 mkm)\n"
						+ "Ks\t1\tApical perception\n"
						+ "Au\t0.0250\tStraightening \n"
						+ "Aude\t4.65\tStraightening decrements\n"
						+ "Sde\t1.57\tCompetence function decay.\n"
						+ "Kr\t20 \tRealization constant\n"
						+ "Tr_Off\t0.0\tAngle from vertical below that transmission is blocked, \n"
						+ "\n" + "Local perception:\n"
						+ "BSde\t4\tDecremet in basip. dir\n"
						+ "Bde\t0.07  \tDecrement from angle\n"
						+ "Bs\t2.4\tLocal perc. const.\n" + "\n"
						+ "Solver parameters:\n"
						+ "Segs\t70\t  (number of segments)\n"
						+ "dT\t0.05\t  \n\n" + "Microgravity simulation: ");

		AddData(
				"C.cinereus, 10 mM BAPTA treatment",
				"Coprinus cinereus stems after \n"
						+ "BAPTA (Ca++ chelator) treatment "
						+ "(10 mM, 2 hours)\n"
						+ "Ks\t1\tApical perception\n"
						+ "Au\t0.0250\tStraightening \n"
						+ "Aude\t8.650\tStraightening decrements\n"
						+ "Sde\t1.2\tSignal weakening during transmission.\n"
						+ "Kr\t61.5 \tRealization constant\n"
						+ "Tr_Off\t0.0\tAngle from vertical below that transmission is blocked, \n"
						+ "\n" + "Local perception:\n"
						+ "BSde\t4\tDecremet in basip. dir\n"
						+ "Bde\t0.085  \tDecrement from angle\n"
						+ "Bs\t0.45\tLocal perc. const.\n" + "\n" + "\n"
						+ "Solver parameters:\n"
						+ "Segs\t150\t  (number of segments)\n" + "dT\t0.05\n");

		AddData(
				"Wheat coleoptiles",
				"3.5 days old wheat coleoptiles \n"
						+ "(not detached from the plant)\n\n"
						+ "Ks\t0.675\tApical perception\n"
						+ "Au\t0.015\tStraightening\n"
						+ "Aude\t1.8\tStraightening decrements\n"
						+ "Sde\t1.8\tSignal weakening during transmission\n"
						+ "Kr\t50\tRealization constant\n"
						+ "Tr_Off\t0.0\tAngle from vertical below that transmission is blocked, \n"
						+ " \n" + "Local signal perception not supposed:\n"
						+ "BSde 0 Bde 0 Bs 0\n" + "Solver parameters:\n"
						+ "Segs\t80\t  (number of segments)\n"
						+ "dT\t0.025\t  \n" + "Microgravity simulation:");
	};

}

RightControls.java Listing
--------------------------------------------------------------

import java.awt.CardLayout;
import java.awt.Color;

/**
 * Control panel at the rights
 */
@SuppressWarnings("serial")
class RightControls extends MyPanel {

	ParaSetControl p;
	XDataSetControl d;

	CardLayout lay;

	public RightControls(GraviJa _app) {

		setBackground(Color.lightGray);
		setLayout(lay = new CardLayout());

		add(d = new XDataSetControl(_app), "D");
		add(p = new ParaSetControl(_app), "P");
		d.app = p.app = _app;
		d.partner = p;
		p.partner = d;
	}

	public String getData() {
		return d.getText();
	};
}

SolvingThread.java Listing
--------------------------------------------------------------

/**
 * The main thread that does the solution.
 */
class SolvingThread extends Thread {
	GraviJa app;

	public SolvingThread(GraviJa _app) {
		super();
		app = _app;
	};

	public void run() {
		synchronized (SolvingThread.class) {
		app.c.solve();
		}
	};
}

TModelBase.java Listing
--------------------------------------------------------------

import java.awt.Dimension;
import java.awt.Graphics;

/**
 * The base of the model. Multiple models were tried deriving them all from this
 * clas.
 */
@SuppressWarnings("serial")
class TModelBase extends ModelCanvas {
	public double radians = Math.PI / 180.0;
	public Graphics dc;

	public TModelBase(GraviJa app) {
		super(app);
		initModel();
	};

	int MaxX;
	int maxY;
	int cpx;
	int cpy;
	int sLH, LH, fX;
	boolean solved = false;
	String Runs = "0";

	public void repaint() {
		dc = getGraphics(); 
		dc.clearRect(0, 0, MaxX, maxY);
		paint(dc);
	};

	public void initModel() {
		// No action by default.
	};

	void beginSolution() {
		// No action by default.
	};

	void DoSolution() {
		// No action by default.
	};

	void finishSolution() {
		repaint(10);
	};

	protected void doSolve() {
		app.controls.B_F9.setLabel("Stop  F9");
		if (!solved)
			initModel();
		beginSolution();
		DoSolution();
		finishSolution();
		solved = true;
		app.controls.B_F9.setLabel("Solve F9");
	};

	// Getting args:
	double Arg(String argn, double x_def) {
		String def = Double.toString(x_def);
		return getArg(argn, def);
	};

	// Graphing:
	void lineTo(int X, int Y) {
		Y = maxY - Y;
		dc.drawLine(cpx, cpy, X, Y);
		cpx = X;
		cpy = Y;
	};

	void moveTo(int X, int Y) {
		Y = maxY - Y;
		cpx = X;
		cpy = Y;
	};

	void line(int X1, int Y1, int X2, int Y2) {
		Y1 = maxY - Y1;
		Y2 = maxY - Y2;
		dc.drawLine(X1, Y1, X2, Y2);
	};

	public void resize(Dimension d) {
		solve();
	};

	// Math:
	// sin, cos argument in DEGREES:
	public double sind(double a) {
		return Math.sin(a * radians);
	};

	public double cosd(double a) {
		return Math.cos(a * radians);
	};

	double abs(double a) {
		return Math.abs(a);
	};

	double exp(double a) {
		return Math.exp(a);
	};

	int Round(double a) {
		return (int) Math.round(a);
	};

}

TModel.java Listing
--------------------------------------------------------------

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.text.NumberFormat;

/**
 * The main mathematical model.
 */
@SuppressWarnings("serial")
class TModel extends TModelBase {
	String r = new String(
			"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[!]"
					+ "^_`abcdefghijklmnopqrstuvwxyz");
	int Zero = r.indexOf('=');
	public boolean XData = false;

	/**
	 * Draw experimental data.
	 */
	public void experiment() { 
		if (charts && !XData)
			return;
		if (charts)
			return;
		if (_0 != 0)
			return;
		if (dc == null)
			dc = getGraphics();

		double hh = _0 < 0 ? (1.0 - sind(_0)) : 1.0;
		double ww = (MaxX < maxY ? MaxX : maxY);
		scale = 0.9 * zoom * ww / hh;

		mX = (int) ((MaxX - scale) * 0.5);
		mY = (int) (_0 < 0 ? -scale * sind(_0) + ww * 0.05 : ww * 0.05)
				+ (int) (maxY * (1.0 - zoom) * .5);

		dc.setColor(Color.gray);
		double x = 0.0; // Pradines koordinates
		double y = 0.0;
		double ax, ay;
		String s = app.rcontrols.d.getText();
		int L = s.length();
		moveTo((int) (x * scale) + mX, (int) (y * scale) + mY);
		for (int k = 0; k < L; k += 2) {
			if ((s.charAt(k)) == '\n') {
				x = y = 0;
				moveTo((int) (x * scale) + mX, (int) (y * scale) + mY);
				if (++k >= L)
					break;
			}
			;
			ax = r.indexOf(s.charAt(k)) - Zero;
			ax *= 0.001;
			ay = r.indexOf(s.charAt(k + 1)) - Zero;
			ay *= 0.001;
			x += ax;
			y += ay;
			lineTo((int) (x * scale) + mX, (int) (y * scale) + mY);
		}
		;
	};

	public void Experiment_Tip() {
		if (!charts || _0 != 0)
			return;
		double x = 0.0; // Pradines koordinates
		double y = 0.0;
		double ax = 0, ay = 0;
		double _ax = 0, _ay = 0;
		int Laik = 0;

		String s = app.rcontrols.d.getText();
		int L = s.length();

		for (int k = 0; k < L; k += 2) {
			if ((s.charAt(k)) == '\n') {
				x = y = 0;
				d_Y[Laik] = Math.atan2(_ay + ay, _ax + ax) / radians;
				d_T[Laik] = Laik * 1.0 / 6.0;
				Laik++;
				if (++k >= L)
					break;
			}
			;
			ax = r.indexOf(s.charAt(k)) - Zero;
			ay = r.indexOf(s.charAt(k + 1)) - Zero;
			x += ax;
			y += ay;
		}
		;
		dS = Laik;
	};

	public TModel(GraviJa _app) {
		super(_app);
	};

	double time;
	double Tip_angle;
	boolean Break_Program = false;

	double Ks; // Sensitvity constant
	double Kr; // Realization constant

	double Cv[]; // Lokalus kreivumas panel
	double La[]; // Lokalus kampas uzpildomas po Cv_La
	double dCv[]; // Lokalus linkimo greitis deg/val
	double Cx[] = dCv;
	double vCv[] = Cv;
	int segs; // Realus segment skaicius. Visada <SILG. ModelBase.
	int HiMax = 200;
	int Hind;
	double Cv_Hi[][] = new double[HiMax + 2][];
	double dCv_Hi[][] = new double[HiMax + 2][];

	int skipc = 0;

	double dT; // Zingsnis laike
	double dX; // Zingsnis erdveje, = 1/Segs
	double T_Out; // Laikas kai sustojama. Jei <0, nesustojama.
	double Time_0; // Pradinio velinimo laikas.
	double wT; // Pagalbinis status eilutes kad per daznai nerodytu

	// (stiebo ilgis laikomas vientu)
	double _0 = 0; // Pagrindo orientacija
	// (svarbu pradzioje nehorizontaliam stiebui)
	int VW = 8; // Visible width
	
	protected boolean dataAvailable() {
		return XData;
	}

	public void paint(Graphics g) {
		dc = g; // if (g!=null) DC=g.create();
		// if (!filled) DC.clearRect(0,0,MaxX,MaxY);
		{
			if (dc == null)
				return;
			if (charts) {
				Experiment_Tip();
				paintCharts(g);
				return;
			}

			// Kampais(g);
			metrics = getFontMetrics(getFont());
			sLH = metrics.getHeight();
			LH = sLH * 3 / 2;
			fX = LH / 4;
			Rectangle r = getBounds();
			/*
			 * vx=fX+SW("000000"); vy=fX; hx=r.width-fX; hy=r.height-2*fX-2*sLH;
			 */
			vx = vy = 0;
			hx = r.width;
			hy = r.height;
			MaxX = hx;
			maxY = hy;
			cpx = cpy = 0;
			dc.setColor(Color.blue.darker());
		}
		if (XData)
			experiment();
		if (Hind > 0) {
			for (int h = 0; h < Hind; h++)
				draw(Cv_Hi[h], dCv_Hi[h]);
		}
	};

	public void beginSolution() {
		super.beginSolution();

		// d_T=d_Y=dE=null;
		// Cv_Hi=dCv_Hi= null;

		dc = getGraphics();
		// Dinaminis masyvu isskyrimas:
		segs = Round(Arg("Segs", 50));
		Cv = new double[segs + 1]; // Lokalus kreivumas
		La = new double[segs + 1]; // Lokalus kampas uzpildomas po Cv_La
		dCv = new double[segs + 1]; // Lokalus linkimo greitis deg/val

		// Cv_Hi =new double[HiMax+2][];
		// dCv_Hi=new double[HiMax+2][];
		Hind = 0;

		dX = 1.0 / segs;
		Kr = Arg("Kr", 100.0);
		Ks = Arg("Ks", 1.0);
		// Segmentu skaicius ir su juo susije dalykai:
		dT = Arg("dT", 0.1);

		Time_0 = 0;
		// Pradine stiebo orientacija - _0:
		_0 = startAngle; // Arg("_0",0.0);
		for (int k = 0; k <= segs; k++)
			La[k] = _0;

		Tip_angle = _0; // Virsunes kampas lygus pagrindo kampui
		time = 0;
		T_Out = finalTime; // Arg("T_Out",50.0);
		wT = 0;
		Break_Program = (time > T_Out) || (T_Out < dT);
		if (app.mine())
			Break_Program &= T_Out > 0; // ///////////// Must be for possibility
		// to solve to infinity
		nSteps = -1;
	};

	void finishSolution() {
		NumberFormat f = NumberFormat.getInstance();
		f.setMaximumFractionDigits(2);
		if (time - dT < 0)
			print("Not solved.");
		else
			print("Finished at time: " + f.format(time - dT)
					+ " hrs. Apex angle: " + f.format(Tip_angle)
					+ " degrees.");
		super.finishSolution();		
	};

	void defaultRun() {
		int k;
		for (k = 0; k <= segs; k++) {
			Cv[k] = Cv[k] + Kr * dCv[k] * dT;
		}
		;
		Cv_La();
		time += dT;

		if (time > wT) {
			wT = time + 1. / 6;
			print("Time: " + Double.toString(Math.round(time * 10) * 0.1)
					+ "h. Apex angle: " + Double.toString(Tip_angle) + " deg.");
			if (nSteps < 100) {
				r_Y[++nSteps] = Tip_angle;
				r_T[nSteps] = time;
			}
			;
		} else
			return;

		if (Hind < HiMax) {
			Cv_Hi[Hind] = new double[segs + 1];
			dCv_Hi[Hind] = new double[segs + 1];
			for (k = 0; k <= segs; k++)
				Cv_Hi[Hind][k] = Cv[k];
			for (k = 0; k <= segs; k++)
				dCv_Hi[Hind][k] = dCv[k];
			Hind++;
		}
		;

		Break_Program = (time > T_Out) && (T_Out > 0);
	};

	void DoSolution() {
		int k;
		while (true) {
			for (k = 0; k <= segs; k++)
				dCv[k] = Ks * cosd(La[k]);
			defaultRun();
			// CAN_BREAK; // OH, Java, Java.. Bent vienakart ijungt neuzmirst!
		}
	};

	void Cv_La() // Apskaiciuja lokaliu kampu masyva is lokaliu
	{
		int k;
		double S = _0;
		for (k = 0; k <= segs; k++) {
			La[k] = S;
			S += Cv[k] * dX;
		}
		;
		Tip_angle = S;
	};

	public int mX, mY;
	double scale; // Mastelis

	int scale(double x) {
		return Round(x * scale * zoom);
	};

	public void draw(double cu[], double dcu[]) // Nupaiso ta stieba. Draws the
	// stem.
	{
		double S = _0;
		double x = 0.0; // Pradines koordinates
		double y = 0.0;
		double hh, ww;

		if ((cu == null) || (dc == null) || charts)
			return;

		hh = _0 < 0 ? (1.0 - sind(_0)) : 1.0;
		ww = (MaxX < maxY ? MaxX : maxY);
		scale = 0.9 * zoom * ww / hh;

		mX = (int) ((MaxX - scale) * 0.5);
		mY = (int) (_0 < 0 ? -scale * sind(_0) + ww * 0.05 : ww * 0.05)
				+ (int) (maxY * (1.0 - zoom) * .5);

		moveTo((int) (x * scale) + mX, (int) (y * scale) + mY);
		for (int k = 0; k <= segs; k++) {
			x += dX * cosd(S);
			y += dX * sind(S);
			if (dcu[k] > 0)
				dc.setColor(Color.blue.darker());
			else
				dc.setColor(Color.red.darker());
			lineTo((int) (x * scale) + mX, (int) (y * scale) + mY);
			S += cu[k] * dX;
		}
		;
	};

}

TopControls.java Listing
--------------------------------------------------------------

import java.awt.Color;
import java.awt.Event;
import java.awt.FlowLayout;
import java.awt.Label;
import java.awt.Panel;

/**
 * Controls at the top
 */
@SuppressWarnings("serial")
class TopControls extends Panel {
	RightControls r;
	// CheckboxGroup cd;
	MyButton button;
	GraviJa app;

	public TopControls(GraviJa _app) {
		app = _app;
		setLayout(new FlowLayout());
		setBackground(Color.lightGray);
		r = new RightControls(app);
		r.lay.addLayoutComponent("Basic hypothesis", r.d);
		r.lay.addLayoutComponent("Model parameters", r.p);

		add(new Label("Show:"));
		add(button = new MyButton(this, "Basic hypothesis"));
		add(new MyButton(this, "Model parameters"));
		if (app.mine())
			add(new MyButton(this, "Summary after solution"));
		// add(new Label("For:"));
		button.Down();
	}

	public boolean act(String label, MyButton nB) {
		// Action:
		r.lay.show(r, label);
		button.Up();
		nB.Down();
		button = nB;
		return true;
	};

	public boolean keyDown(Event e, int k) {
		if (k == Event.F9) {
			app.controls.resolve();
			return true;
		}
		return false;
	};
}

wheat.txt Listing
--------------------------------------------------------------

3.5 days old wheat coleoptiles \n"+
"Ks\t0.675\tSensitivity constant\n"+
"Au\t0.015\tAutotropism\n"+
"Aude\t1.8\tAutotropism decrements\n"+
"Sde\t1.5\tCompetence function decay\n"+
"Kr\t50\tRealization constant\n"+
"Tr_Off\t8.0\tAngle from vertical below that transmission is blocked, \n"+
"n_Para\t4\n"+
" \n"+
"Stem position:\n"+
 "Apex_Pinned\t0 ( 0 for base-, 1 for apex- pined stem)\n"+
"Local signal perception\n"+
 "BSde\t0\n"+
"Bde\t0\n"+
"Bs\t0\n"+
"tt\t0\n"+
 "Solver parameters:\n"+
"Segs\t80\t  (number of segments)\n"+
"dT\t0.025\t  (step in time)\n"+
"\n"

XDataSetControl.java Listing
--------------------------------------------------------------

import java.awt.BorderLayout;

/**
 * Experiment data set control.
 */
@SuppressWarnings("serial")
class XDataSetControl extends DataSetControl {
	public ConfigBank Config;

	public XDataSetControl(GraviJa _app) {
		setLayout(new BorderLayout());
		add("North", DataSet);
		// add("Center",Dp);
		add("Center", Config = new ConfigBank());
		Config.app = app = _app;
	    Config.ad(Config.Sh_Tip); 
		Config.Reset();
		Dp.setLayout(l);
		l.show(Dp, Data[0].n);
		DataOnTop = Data[0].n;
	}

	public Bank getBank(String name, String data) {
		return new DataBank(name, data);
	}

	void AddAllData() {
		Experimental E = new Experimental();
		AddData("Coprinus cinereus stems, air control", E.C_air());
		AddData("C.cinereus, water treatment", E.C_Water());
		AddData("C.cinereus, 10 mkm cytochalazin D treatment", E.C_CyD_10());
		AddData("C.cinereus, 1 mkm cytochalazin D treatment", E.C_CyD_1());
		AddData("C.cinereus, A231817 treatment", E.C_A231817());
		AddData("C.cinereus, 10 mM BAPTA treatment", E.C_BAPTA());
		AddData("Wheat coleoptiles", E.W_air());
	};
}

--------------------------------------------------------------
This concludes the file listings for subdirectory compensation/src/

This concludes the Program listings for the model using curvature compensation to simulate the spatial organisation of the gravitropic reaction

Program listings for the model simulating the apex angle of the gravitropic reaction
Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

Limitation of Liability.
  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

Program and coding Copyright (C) Audrius Meškauskas 2017
Text Copyright (C) David Moore 2017
All other rights and trademarks of third parties acknowledged
--------------------------------------------------------------

Directory Structure

C:/apex/

--------------------------------------------------------------
Program listings
--------------------------------------------------------------

This is a program listing of the .java files required for the model simulating the apex angle of the gravitropic reaction

The following 15 .java files are included in this listing in alphabetical order:

ArcCanvas.java
ArcControls.java
Bank.java
DataBank.java
DataSetControl.java
DataSize.java
Main.java
MButton.java
MPanel.java
ParaBank.java
ParaSetControls.java
RightControls.java
ThreeElementPanel.java
TwoElementPanel.java
XDataSetControl.java
--------------------------------------------------------------

ArcCanvas.java Listing
--------------------------------------------------------------

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Event;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;

class ArcCanvas extends Canvas {
	// ************* Model parameters ***********
	double Kw, K, beta, t_, G = 1.0;
	// ******************************************
	double startAngle = 0;
	double finalTime = 6;

	double finalAngle = 360;
	double initialAngle = -360;
	double maxAngleChange = 1;
	// ******************************************
	boolean filled = false;

	int laikas = 100; // Number of steps
	double rT[] = new double[laikas + 1]; // times
	double rY[] = new double[laikas + 1]; // Y's

	double dT[] = new double[laikas + 1]; // Data times
	double dY[] = new double[laikas + 1]; // Data Y's
	double dE[] = new double[laikas + 1]; // Standard deviations

	int dS = 0; // Data count

	int XMA = 120;
	boolean IntKey = false;
	FontMetrics Metras;
	Main app;
	int vx, vy, hx, hy;

	public ArcCanvas(Main _app) {
		app = _app;
		setBackground(Color.white);
		for (int k = 0; k <= laikas; k++)
			rT[k] = rY[k] = 17.0;
	};

	double smx = 1, smy = 1, siay = 0;
	int sdx = 0, sdy = 0, sry = XMA;

	int gx(double x) {
		return (int) Math.round(smx * x) + sdx;
	};

	int gy(double y) {
		return sry - (int) Math.round(smy * (y - siay)) + sdy;
	};

	int SW(String s) {
		return Metras.stringWidth(s);
	};

	void print(String s) {
		//if (app != null)
        //	app.showStatus(s);
	};

	void Print(String s) {
		// if (app != null)
		//	app.Conclude(s);
	};

	public boolean keyDown(Event e, int k) {
		if (k == Event.F9) {
			app.controls.resolve();
			return true;
		}
		;
		return false;
	};

	public boolean mouseMove(Event e, int x, int y) {
		Double xx = new Double((x - sdx) / smx);
		Double yy = new Double((-y + sry + smy * siay + sdy) / smy);
		print("The mouse is pointing at time " + xx.toString() + " angle " + yy.toString());
		return true;
	};

	public boolean mouseExit(Event e, int x, int y) {
		print("");
		return true;
	};

	boolean space(String s, int i) {
		return Character.isSpace(s.charAt(i));
	};

	private String _d_buf = "";
	double _d_time;
	double _d_dat;
	double _d_se;
	int _d_I;
	int _d_L;

	boolean Space(char c) {
		if (Character.isSpace(c))
			return true;
		if (c == '\n' || c == '\r')
			return true;
		return false;
	};

	boolean Skip_Spaces() {
		if (_d_I >= _d_L)
			return false;
		while (space(_d_buf, _d_I) && (_d_I < _d_L))
			_d_I++;
		if (_d_I >= _d_L)
			return false;
		if (_d_buf.charAt(_d_I) == '(')
			return false;
		return true;
	};

	boolean _nextData() {
		if (!Skip_Spaces())
			return false;
		int K = _d_I;
		int D;
		String s;
		D = K;

		while ((D < _d_L) && Space(_d_buf.charAt(D))) {
			D++;
			if (D > _d_L)
				return false;
		}
		;

		if (D >= _d_L)
			return false;
		if (_d_buf.charAt(K) == '(')
			return false;

		while (!Space(_d_buf.charAt(D))) {
			D++;
			if (D > _d_L)
				return false;
		}
		;
		if (_d_buf.charAt(D - 1) == '(')
			return false;

		s = _d_buf.substring(K, D);
		_d_time = app.toDouble(s);

		while ((D < _d_L) && Space(_d_buf.charAt(D))) {
			if (D > _d_L)
				return false;
			D++;
		}
		;
		K = D;

		while (!Space(_d_buf.charAt(D))) {
			D++;
			if (D > _d_L)
				return false;
		}
		;
		s = _d_buf.substring(K, D);
		_d_dat = app.toDouble(s);
		while ((D < _d_L) && Space(_d_buf.charAt(D))) {
			D++;
			if (D > _d_L)
				return false;
		}
		;
		K = D;

		while (!Character.isSpace(_d_buf.charAt(D))) {
			if (D > _d_L)
				return false;
			D++;
		}
		;
		s = _d_buf.substring(K, D);
		_d_se = app.toDouble(s);

		K = D;
		_d_I = D;
		return true;
	};

	public boolean getData() // dS - duomenu skaicius skaiciuojnt NUO NULIO
	{
		int k;
		dS = 0;
		_d_buf = app.rcontrols.d.getText() + " "; // Paraunam argumentu teksta
		_d_buf.trim();
		_d_I = 0;
		_d_L = _d_buf.length() - 1;
		if (_d_L <= 0) {
			dS = 0;
			return false;
		}
		;
		for (k = 0; k <= 100; k++) {
			if (!_nextData())
				break;
			dT[k] = _d_time;
			dY[k] = _d_dat;
			dE[k] = _d_se;
		}
		;
		dS = k - 1;
		return (dS != 0);
	};

	public double getArg(String arg, String Default) // Get parameter
	{
		RightControls Q = app.rcontrols;
		String s = " " + Q.p.getText(); // get text of the parameter
		Double r = new Double(55.17);
		arg.trim();
		int k, l;
		int i = 0;
		if (s.length() <= arg.length() + 3)
			i = -1;
		else {
			l = arg.length();
			while (i < s.length() - l - 1) {
				i = s.indexOf(arg, i);
				if (i < 0)
					break;
				if (space(s, i - 1) && space(s, i + l))
					break;
				i += l;
			}
			;
		}
		if (i < 0) {
			Q.p.appendText('\n' + arg + '\t' + Default);
			r = Double.valueOf(Default);
		} else {
			// s = (s.substring(i+arg.length()));
			l = s.length();
			i += arg.length();

			for (k = i; k < l; k++) {
				if (!Character.isSpace(s.charAt(k)))
					break;
			}
			;
			i = k;
			for (k = i; k < l; k++) {
				if (Character.isSpace(s.charAt(k)))
					break;
			}
			;
			s = s.substring(i, k);
			r = Double.valueOf(s);

		}
		;
		return r.doubleValue();
	};

	double sqr(double x) {
		return x * x;
	};

	// Delay system:
	int vrange = 4 * laikas;
	double vY[] = new double[vrange + 1]; // delay buffer, parameter
	double vT[] = new double[vrange + 1]; // delay buffer, time
	int spos, rpos; // read and write positions

	void InitDelay() {
		vrange = 4 * laikas;
		vY = new double[vrange + 1]; // delay buffer, parameter
		vT = new double[vrange + 1]; // delay buffer, time

		for (int k = 0; k <= vrange; k++)
			vY[k] = vT[k] = 0;
		spos = rpos = 0;
		t_ = getArg("T_delay", "0.33");
	};

	void Remember(double t, double y) {
		vY[spos] = y;
		vT[spos++] = t;
	};
	/* no need to keep, better long buffer */

	double Delayed(double t) {
		if (t < 0)
			return 0;
		while ((rpos < spos) && (vT[rpos] < t))
			rpos++;
		if (rpos == spos)
			return 0;
		else
			return vY[rpos];
	};

	public void redraw(boolean filled, double start_a, double end_t) {
		this.filled = filled;
		this.finalTime = end_t;
		this.startAngle = start_a;
		// System.out.println("Resolving...");
		Solve();
	};

	public void paint(Graphics g) {

		if (finalTime < 0)
			redraw(filled, startAngle, finalTime); // First initialization, not required

		int i, j, k, l; // Ciklams
		Metras = getPeer().getFontMetrics(g.getFont());
		int sLH = Metras.getHeight();
		int LH = sLH * 3 / 2;
		int fX = (LH / 8) * 2;
		if (fX < 2)
			fX = 2;

		// nustatom y intervala:
		finalAngle = initialAngle = rY[0];
		for (k = 1; k <= laikas; k++) {
			if (rY[k] > finalAngle)
				finalAngle = rY[k];
			if (rY[k] < initialAngle)
				initialAngle = rY[k];
		}
		;
		// ir duomenu reik n'uzmirst
		if (getData()) {
			for (k = 0; k <= dS; k++) {
				if (dY[k] > finalAngle)
					finalAngle = dY[k];
				if (dY[k] < initialAngle)
					initialAngle = dY[k];
			}
			;
		}
		;

		long a = Math.round((initialAngle));
		long b = Math.round((finalAngle));
		long c = (b - a) / 2;
		long ak = c > 30 ? 30 : 3;
		long v = (b + a) / 2;
		v = (v / ak) * ak;
		c = (c / ak) * ak;

		c += ak;

		a = v - c;
		b = v + c;

		initialAngle = a;
		finalAngle = b;

		/****** Drawing axes ***********/

		/*
		 * butu idomu zinoti is kur bound() zino kieno ribas rast. Faktas, zino
		 */
		Rectangle r = bounds();
		g.setColor(Color.black);
		vx = fX + SW("000000");
		vy = fX;
		hx = r.width - fX;
		hy = r.height - 2 * fX - 2 * sLH;
		g.drawLine(vx, vy, vx, hy);
		g.drawLine(vx, hy, hx, hy);
		int y = hy, x = vx, dx = ((hx - vx) * 95) / (5 * 100);

		smx = 0.95 * (hx - vx) / finalTime;
		sdx = vx;
		for (k = 0; k <= 5; k++) // Horizontali asis
		{
			double vT = k * (finalTime / 5);
			x = gx(vT);
			g.drawLine(x, y - fX, x, y + fX);
			g.drawString(Double.toString(Math.round(vT * 100) / 100), x, y + LH);
		}
		;
		g.drawString("Time", (x - vx) / 2, hy + 2 * sLH + fX);
		x = vx;

		double rAngle = finalAngle - initialAngle;
		smy = 0.95 * (hy - vy) / rAngle;
		sdy = vy;
		sry = hy - fX;
		siay = initialAngle;
		double dy = Math.round(rAngle / 6);
		int ddT = SW("-120 o");
		int ddL = x - SW("o ");
		for (k = 0; k <= 6; k++) // Vertikali asis
		{
			double ly = k * dy + initialAngle;
			y = gy(ly);
			g.drawLine(x - fX, y, x + fX, y);
			g.drawString(Long.toString(Math.round(ly)), x - ddT, y - fX);
			g.drawString("o", ddL, y - 3 * fX);

		}
		;

		/*
		 * String leg="Apex angle,o"; x=fX; y=vy+LH; y=
		 * ((hy-y)-(leg.length()*sLH))/2; for (k=0;k<leg.length();k++) {
		 * g.drawString(leg.substring(k,k+1),x,y); y+=sLH; };
		 */

		/****** Drawing model ***********/

		g.setColor(Color.red.darker());
		int xx = gx(rT[0]), yy = gy(rY[0]);
		for (k = 1; k <= laikas; k++) {
			g.drawLine(xx, yy, xx = gx(rT[k]), yy = gy(rY[k]));
		}
		;

		/****** Drawing data ***********/
		if (getData()) {
			g.setColor(Color.blue.darker());
			for (k = 0; k <= dS; k++) {
				int _ly = gy(dY[k]), _lx = gx(dT[k]), _le = gy(dE[k]) - gy(0);
				g.fill3DRect(_lx - fX / 2, _ly - fX / 2, fX, fX, true);
				g.drawLine(_lx, _ly + _le, _lx, _ly - _le);
			}
			;
		}
		;
	};

	void Solve() {
		double H, X, v, w, Y, z;
		double f, a, b, c;
		int tt;
		boolean finished;

		laikas = 100;

		for (;;) {
			finished = true;

			// { Runge Kuto Beisikinis }
			H = finalTime / laikas; // _H;
			X = 0; // _X0;
			w = startAngle; // _Y0;
			v = 0; // _DYDX0;
			Y = w;
			z = v;

			tt = 0;
			InitDelay();

			rT[tt] = X;
			rY[tt] = Y;
			tt++;

			Kw = getArg("Kw", "50.0");
			K = getArg("K", "535");
			// G= getArg("g","1.0");
			beta = getArg("beta", "0");

			int xx = gx(0), yy = gy(w); // Pirmas taskas
			IntKey = false;
			do {
				f = Fx(X, Y, z);
				Remember(X, Perception(Y));
				a = H * f;
				X = X + H / 2;
				Y = w + v * H / 2 + a * H / 8;
				z = v + a / 2;
				Remember(X, Perception(Y));
				f = Fx(X, Y, z);
				b = H * f;
				z = v + b / 2;
				f = Fx(X, Y, z);
				c = H * f;
				X = X + H / 2;
				Remember(X, Perception(Y));
				Y = w + H * v + H * c / 2;
				z = v + c;
				f = Fx(X, Y, z);
				Y = w + H * (v + (a + b + c) / 6);
				w = Y;
				z = v + (a + (b + c) * 2 + H * f) / 6;
				v = z;

				rT[tt] = X;
				rY[tt] = Y;

				if (Math.abs(rY[tt] - rY[tt - 1]) > maxAngleChange) {
					laikas *= 2;
					finished = false;
					rT = new double[laikas + 1]; // laikai
					rY = new double[laikas + 1]; // Y'ai
					print("Reducing step...");
					break;
				}
				;
				tt++;

				// app.Conclude(Double.toString(Delayed(X-t_)));
				if (tt % 20 == 0)
					print(Double.toString(100 * tt / laikas));

			} while (!(/* (X>finalTime) || */ IntKey || (tt > laikas)));

			if (finished) {
				break;
			}
			;
		}
		;
		print("Steps done: " + Integer.toString(laikas));
		System.gc();
		repaint();
	};

	double Perception(double alpha) {
		return -K * G * Math.sin((alpha - beta) * Math.PI / 180.0);
	};

	double Fx(double X, double Y, double Y_)
	// { Matematinio modelio funkcija }
	{
		double ret;
		// ret=-sqr(2*3.114159/(Kw))*Y-0.01*Y_; return ret;
		return (Delayed(X - t_)) - Kw * Y_;
	};
}

ArcControls.java Listing
--------------------------------------------------------------

import java.awt.Button;
import java.awt.Checkbox;
import java.awt.Color;
import java.awt.Component;
import java.awt.Event;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Label;
import java.awt.TextField;

class ArcControls extends MPanel {
	private static final String ANGLE = " \u00b0";
	TextField a, t, g;
	GridBagLayout Bag;

	ArcCanvas canvas;

	Button Bp = new Button("+");
	Button Bm = new Button("-");

	Button Tp = new Button("+");
	Button Tm = new Button("-");

	Button Gp = new Button("+");
	Button Gm = new Button("-");

	Button bReset = new Button("Reset");
	Button bResolve = new Button("Resolve F9");

	public Checkbox qf = new Checkbox("Show experimental data");

	public ArcControls(ArcCanvas canvas) {
		this.canvas = canvas;
		Component ia;
		setLayout(Bag = new GridBagLayout());

		setBackground(Color.lightGray);

		add(new TwoElementPanel(new Label("Initial angle, o"),
				new ThreeElementPanel(Bp, a = new TextField(Double.toString(canvas.startAngle) + ANGLE, 4), Bm)));
		add(new Label(" "));

		add(new TwoElementPanel(ia = new Label("Final time, h"),
				new ThreeElementPanel(Tp, t = new TextField(Double.toString(canvas.finalTime) + " h", 4), Tm)));
		add(new Label(" "));

		add(new TwoElementPanel(new Label("Gravity vector "),
				new ThreeElementPanel(Gp, g = new TextField(Double.toString(canvas.G) + " g", 4), Gm)));
		add(new Label(""));
		
		add(bResolve);
		
		add(new Label(" "));
		
		add(bReset);
		add(new Label(" "));

		GridBagConstraints c = new GridBagConstraints();
		c.weightx = 1;
		Bag.setConstraints(ia, c);
	
		c = new GridBagConstraints();
		c.weightx = 1;
		c.fill = GridBagConstraints.VERTICAL;
		c.ipadx = 10;
		c.insets = new Insets(3, 10, 3, 10);
		c.anchor = GridBagConstraints.WEST;
		Bag.setConstraints(bResolve, c);		
		
		c = new GridBagConstraints();
		c.weightx = 1;
		c.fill = GridBagConstraints.VERTICAL;
		c.ipadx = 10;
		c.insets = new Insets(3, 10, 3, 10);
		c.anchor = GridBagConstraints.EAST;
		Bag.setConstraints(bReset, c);		
	};

	public void resolve() {
		canvas.G = app.toDouble(g.getText());
		canvas.redraw(qf.getState(), app.toDouble(a.getText()), app.toDouble(t.getText()));
	};

	double inc(double x, double delta, double scale) {
		x += delta;
		return x = Math.round(x / scale) * scale;
	};

	public boolean action(Event ev, Object arg) {
		if (ev.target instanceof Button) {
			// String label = (String)arg;
			if (ev.target == Gp)
				g.setText(Double.toString(inc(canvas.G, 0.1, 0.0001)) + " g");
			if (ev.target == Gm)
				g.setText(Double.toString(inc(canvas.G, -0.1, 0.0001)) + " g");
			if (ev.target == Bp)
				a.setText(Double.toString(inc(canvas.startAngle, 15, 0.01)) + ANGLE);
			if (ev.target == Bm)
				a.setText(Double.toString(inc(canvas.startAngle, -15, 0.01)) + ANGLE);
			if (ev.target == Tp)
				t.setText(Double.toString(inc(canvas.finalTime, 0.5, 0.01)) + " h");
			if (ev.target == Tm)
				t.setText(Double.toString(inc(canvas.finalTime, -0.5, 0.01)) + " h");
			if (canvas.finalTime < 0)
				t.setText(Double.toString(canvas.finalTime = 0) + " ?");

			if (ev.target == bReset) {
				g.setText(Double.toString(canvas.G = 1.0) + " g");
				a.setText(Double.toString(canvas.startAngle = 0.0) + ANGLE);
				t.setText(Double.toString(canvas.finalTime = 6.0) + " h");
				app.rcontrols.p.Reset();
				app.rcontrols.d.Reset();
			}
			
			resolve();
			return true;
		}

		return false;
	};

}

Bank.java Listing
--------------------------------------------------------------

import java.awt.BorderLayout;
import java.awt.Panel;
import java.awt.TextArea;

class Bank extends Panel {
	public Bank(String name, String data) {
		setLayout(new BorderLayout());
		add("Center", t = new TextArea(data, 12, 24));
		n = name;
	}

	public String n;
	public TextArea t;
}

DataBank.java Listing
--------------------------------------------------------------

import java.awt.Label;

class DataBank extends Bank {
	public DataSize nt;

	public DataBank(String name, String data) {
		super(name, data);
		add("North", new Label("Format: <time> <angle> <stdev of mean>. "));
		// add("South", nt=new _DataSize("Stems measured:", 12));
		n = name;
	};
}

DataSetControl.java Listing
--------------------------------------------------------------

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Choice;
import java.awt.Event;
import java.awt.Panel;

class DataSetControl extends MPanel {
	public Bank getBank(String name, String data) {
		return new Bank(name, data);
	}

	Choice DataSet = new Choice();
	private Panel Dp = new Panel();
	private Bank Data[] = new Bank[20];
	CardLayout l = new CardLayout();
	DataSetControl friend = null;
	String DataOnTop = "Raw init";
	int DI = -1; // Paskutinio banko indeksas, -1 jei nieko nera
	boolean restarting = false;

	public DataSetControl() { // Bicho DataSet tokia pati ir junginejasi taip
								// pat
		setLayout(new BorderLayout());
		add("North", DataSet);
		add("Center", Dp);
		Dp.setLayout(l);
		AddAllData();
		l.show(Dp, Data[0].n);
		DataOnTop = Data[0].n;
	};

	public void Reset() {
		DI = -1;
		restarting = true;
		AddAllData();
	};

	Bank top() {
		int k;
		for (k = 0; k <= DI; k++) {
			if (Data[k].n.compareTo(DataOnTop) == 0)
				return Data[k];
		}
		;
		return null;
	};

	String getText() {
		return top().t.getText();
	};

	void appendText(String s) {
		top().t.appendText(s);
	};

	public void AddData(String name, String data) {
		if (restarting) {
			Data[++DI].t.setText(data);
		} else {
			Data[++DI] = getBank(name, data);
			if (DataSet != null)
				DataSet.addItem(name);
			if (l != null)
				l.addLayoutComponent(name, Data[DI]);
			if (Dp != null)
				Dp.add(Data[DI]);
		}
		;
	}

	void AddAllData() {
		AddData("Triticum aestivum coleoptiles", "");
		AddData("Coprinus Cinereus stems", "");
		AddData("User defined data", "");
		AddData("Help on data definition", "");
	};

	public boolean action(Event ev, Object arg) {
		if (ev.target instanceof Choice) {
			String label = (String) arg;
			// Action:
			l.show(Dp, label);
			DataOnTop = label;
			friend.DataOnTop = label;
			friend.DataSet.select(label);
			friend.l.show(friend.Dp, label);
			app.controls.resolve();
			return true;
		}
		return false;
	};

	public boolean keyDown(Event e, int k) {
		if (k == Event.F9) {
			app.controls.resolve();
			return true;
		}
		;
		return false;
	};

}

DataSize.java Listing
--------------------------------------------------------------

import java.awt.Color;
import java.awt.Label;
import java.awt.TextField;

class DataSize extends MPanel {
	public void setNt(int _Nt) {
		nt.setText(Integer.toString(_Nt));
	};

	public TextField nt;
	public Label Zyme;

	public DataSize(String objName, int _def) {
		setBackground(Color.lightGray);
		add(Zyme = new Label(objName));
		add(nt = new TextField(Integer.toString(_def)));
	};

}

Main.java Listing
--------------------------------------------------------------

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Event;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;

import javax.swing.JFrame;

/* as taip suprantu visa grafika sukista i canvas: matyt kazkam to reikia */

public class Main extends Panel {
	ArcControls controls;
	RightControls rcontrols;
	TopControls tcontrols;
	ArcCanvas c;

	public void init() {
		setLayout(new BorderLayout());

		add("Center", c = new ArcCanvas(this));
		add("North", tcontrols = new TopControls(this));
		add("East", rcontrols = tcontrols.R); 
		add("South", controls = new ArcControls(c));

		c.app = tcontrols.app = rcontrols.app = controls.app = this;

		c.Solve();
	};

	public void start() {
		controls.enable();
	}

	public void stop() {
		controls.disable();
	}

	String num = "0123456789.+-Ee";

	double toDouble(String s) {
		int L = s.length();
		char S[] = s.toCharArray();
		for (int k = 0; k < L; k++) {
			if (num.indexOf(S[k]) < 0)
				S[k] = ' ';
		}
		;
		int K = 0, D = L - 1;
		while (Character.isSpace(S[K]))
			K++;
		while (Character.isSpace(S[D]))
			D--;

		s = s.copyValueOf(S, K, D - K + 1);
		// Conclude(">"+s+"<");
		return Double.valueOf(s).doubleValue();
	}

	public static void main(String args[]) {
		JFrame f = new JFrame("Apex angle-based model");
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		Main AGravija = new Main();
	
		System.out.println("Starting init:");
		AGravija.init();
		AGravija.start();
	
		f.add("Center", AGravija);
		f.setBounds(20, 20, 800, 600);
		f.setVisible(true);
	};

};

MButton.java Listing
--------------------------------------------------------------

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;

class MButton extends Canvas {
	String Title;
	boolean Deep = false;
	TopControls t;
	int dx = 0;

	public Dimension minimumSize() {
		Font fontas = getPeer().getGraphics().getFont();
		FontMetrics f = getPeer().getFontMetrics(fontas);
		dx = f.getHeight() / 2;
		return new Dimension(f.stringWidth(Title) + 2 * dx, (f.getHeight() + 2 * dx));
	};

	public Dimension preferredSize() {
		return minimumSize();
	};

	public MButton(TopControls at, String s) {
		Title = s;
		t = at;
	};

	public void paint(Graphics g) {
		Rectangle r = bounds();
		int hx = r.width, hy = r.height;
		g.setColor(Color.lightGray);
		g.fill3DRect(0, 0, hx, hy, !Deep);
		g.setColor(Color.black);
		g.drawString(Title, dx, hy - dx * 3 / 2);
		if (Deep) {
			g.drawLine(0, 0, 0, hy);
			g.drawLine(0, 0, hx, 0);
		}
		;
	};

	public boolean mouseDown(Event ev, int x, int y) {
		t.act(Title, this);
		repaint();
		return true;
	};

	void Up() {
		Deep = false;
		repaint();
	};

	void Down() {
		Deep = true;
		repaint();
	};

}

MPanel.java Listing
--------------------------------------------------------------

import java.awt.Panel;

class MPanel extends Panel {
	Main app;

	public MPanel() {
	};
}

ParaBank.java Listing
--------------------------------------------------------------

import java.awt.Label;

class ParaBank extends Bank {
	public ParaBank(String name, String data) {
		super(name, data);
		add("North", new Label("Model parameters:"));
	}
}

ParaSetControls.java Listing
--------------------------------------------------------------

class ParaSetControl extends DataSetControl {
	public Bank getBank(String name, String data) {
		return new ParaBank(name, data);
	}

	void AddAllData() {
		AddData("Coprinus Cinereus stems",
				"K\t285 Sensitivity to gravity\n\t    constant\n\n"
						+ "beta\t90 \u00b0 Hypthetical angle of\n\t   plagiogravitropic response\n\t   (here 90 means strictly\n\t  negative gravitropism)\n\n"
						+

						"T_delay\t0.33 h Time after that the signal \n\t   from the apex reaches\n\t  the bending point\n\n"
						+ "Kw 8.2\tSignal weakening constant\n");

		AddData("Triticum aestivum coleoptiles",
				"K\t735 Sensitivity to gravity\n\t    constant\n\n"
						+ "beta\t90 \u00b0 Hypthetical angle of\n\t   plagiogravitropic response\n\t   (here 90 means strictly\n\t  negative gravitropism )\n\n"
						+

						"T_delay\t0.01 Time after that the signal \n\t   from the tip reaches\n\t  the bending point\n\n"
						+ "Kw 50\tSignal weakening constant\n");

		// "T_delay\t0.01\n"+"Kw\t50.0\n"+"K\t735\n"+"g\t1.0\n"+"beta\t90\n");

		AddData("Triticum aestivum roots",

				"K\t500 Sensitivity to gravity\n\t    constant\n\n"
						+ "beta\t50 \u00b0 Hypthetical angle of\n\t   plagiogravitropic response\n\t   (the roots are plagiogravitropic)\n\n"
						+

						"T_delay\t0.01 h Time after that the signal \n\t   from the tip reaches\n\t  the bending point\n\n"
						+ "Kw 12\tSignal weakening constant\n");

		// "T_delay\t0.01\n"+"Kw\t12.0\n"+"K\t500\n"+"g\t1.0\n"+"beta\t50\n");

		AddData("Zea mays roots",
				"K\t176 Sensitivity to gravity\n\t    constant\n\n"
						+ "beta\t72 \u00b0 Hypthetical angle of\n\t   plagiogravitropic response\n\t   (the roots are plagiogravitropic)\n\n"
						+

						"T_delay\t0.198 h Time after that the signal \n\t   from the tip reaches\n\t  the bending point\n\n"
						+ "Kw 2.99\tSignal weakening constant\n");

		AddData("New object",
				"K\t176 Sensitivity to gravity\n\t    constant\n\n"
						+ "beta\t72 \u00b0 Hypthetical angle of\n\t   plagiogravitropic response\n\t   (the roots are plagiogravitropic)\n\n"
						+

						"T_delay\t0.198 h Time after that the signal \n\t   from the tip reaches\n\t  the bending point\n\n"
						+ "Kw 2.99\tSignal weakening constant\n" + "\nHere are the suggested values"
						+ "\nfor beginning. Try to change them" + "\nto reach the best fitting."
						+ "\nAutomatic fitting is not available" + "\nin this simple demonstration program");

		// "K\t 176.875 \n beta\t72\nT_delay\t 0.198 \n Kw\t 2.99\n(Stockus,
		// 1993)");

		/*
		 * AddData("Helianthus anuus hypocotyls",
		 * "K\t   535 \n T_delay\t 0.292  \n Kw\t 3.21"); AddData(
		 * "Lepidum sativum roots", "K\t 750.5 \n T_delay\t 0.03  \n Kw\t 11.8"
		 * ); AddData("Avena sativa coleoptiles",
		 * "K\t 112   \n T_delay\t 0.119 \n Kw\t 3.17");
		 */
	};

}

RightControls.java Listing
--------------------------------------------------------------

import java.awt.CardLayout;
import java.awt.Color;
import java.awt.TextArea;

class RightControls extends MPanel {

	// DataSetControl d,p;
	ParaSetControl p;
	XDataSetControl d;

	CardLayout Lay;

	public RightControls(Main _app) {
		setBackground(Color.lightGray);
		setLayout(Lay = new CardLayout());
 
		add(d = new XDataSetControl());
		add(p = new ParaSetControl());
		d.app = p.app = _app;
		d.friend = p;
		p.friend = d;
	}

	public String getData() {
		return d.getText();
	};
}

ThreeElementPanel.java Listing
--------------------------------------------------------------

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Panel;

class ThreeElementPanel extends Panel {
	public ThreeElementPanel(Component C1, Component C2, Component C3) {
		setLayout(new BorderLayout());
		add("East", C1);
		add("Center", C2);
		add("West", C3);
	};
}

TwoElementPanel.java Listing
--------------------------------------------------------------

import java.awt.Component;
import java.awt.GridLayout;
import java.awt.Panel;

class TwoElementPanel extends Panel {
	public TwoElementPanel(Component C1, Component C2) {
		setLayout(new GridLayout(2, 1));
		add(C1);
		add(C2);
	};
}

XDataSetControl.java Listing
--------------------------------------------------------------

class XDataSetControl extends DataSetControl {
	public Bank getBank(String name, String data) {
		return new DataBank(name, data);
	}

	void AddAllData() {

		AddData("Coprinus Cinereus stems", "0\t-4.6\t4\n" + "0.16\t-3.6\t3.2\n" + "0.33\t-0.4\t4.2\n" + "0.5\t3\t3.1\n"
				+ "0.66\t10.1\t4.5\n" + "0.83\t19.8\t4.6\n" + "1\t27.4\t3.9\n" + "1.16\t32.1\t3.6\n"
				+ "1.33\t36.2\t3.8\n" + "1.5\t40.4\t4.5\n" + "1.66\t44.5\t5.1\n" + "1.83\t47.8\t6.5\n"
				+ "2\t49.2\t2.7\n" + "2.16\t52.6\t2.3\n" + "2.33\t58.2\t7.5\n" + "2.5\t61.2\t6.5\n" + "2.66\t62.8\t6\n"
				+ "2.83\t64.5\t6.8\n" + "3\t66.1\t6.7\n" + "3.16\t68.7\t6.2\n" + "3.33\t74.4\t7\n" + "3.5\t81.2\t9.1\n"
				+ "3.66\t85.6\t7.6\n" + "3.83\t88.9\t6.6\n" + "4\t92.8\t7.2\n" + "4.16\t96.3\t6.7\n" + "4.33\t99.1\t6\n"
				+ "4.5\t101.2\t5.6\n" + "4.66\t102.8\t5.9\n" + "4.83\t104.2\t6\n" + "5\t105.4\t6.5\n"
				+ "5.16\t106.2\t6.6\n" + "5.33\t107.6\t6\n" + "5.5\t110.2\t8.3\n" + "5.66\t112.1\t7.8\n"
				+ "5.83\t113.6\t8.4\n" + "6\t114.9\t8.7\n" + "6.16\t115.8\t9.3\n" + "(Novak Frazier, 1996)");

		AddData("Triticum aestivum coleoptiles",
				"0\t6.6\t2\n" + "0.16\t6.6\t2.3\n" + "0.33\t6.9\t2\n" + "0.5\t8.2\t2.6\n" + "0.66\t9.5\t4.3\n"
						+ "0.83\t9.7\t2.3\n" + "1\t11.1\t1.9\n" + "1.16\t13.6\t3.8\n" + "1.33\t14.4\t3.5\n"
						+ "1.5\t15.1\t1.3\n" + "1.66\t19\t3.9\n" + "1.83\t22.9\t4.9\n" + "2\t24.8\t4.3\n"
						+ "2.16\t26.4\t2.9\n" + "2.33\t29.7\t6.6\n" + "2.5\t31.5\t4.1\n" + "2.66\t34.4\t6.5\n"
						+ "2.83\t37.7\t7.1\n" + "3\t40\t6.2\n" + "3.16\t42\t7.1\n" + "3.33\t44.3\t6.8\n"
						+ "3.5\t46.8\t6.9\n" + "3.66\t48.8\t7.3\n" + "3.83\t48.7\t6.9\n" + "4\t49\t6\n"
						+ "4.16\t50.3\t7.3\n" + "4.33\t50.3\t5.8\n" + "4.5\t52\t5.2\n" + "4.66\t54.8\t7.4\n"
						+ "4.83\t55.9\t4.6\n" + "5\t57.9\t6.1\n" + "5.16\t59.3\t6.2\n" + "(Jurkoniene, 1997)");

		AddData("Triticum aestivum roots",
				"0\t12.4\t4.3\n" + "1\t29.1\t5.5\n" + "2\t39.2\t4.2\n" + "3\t45.6\t2.4\n" + "4\t47\t3.3\n"
						+ "5\t47\t3.1\n" + "6.16\t60.3\t4.1\n" + "7\t51.4\t5.8\n" + "8.66\t39.7\t7.6\n"
						+ "10.16\t46.6\t9.5\n" + "11.25\t46.7\t9.1\n" + "12.33\t47.4\t8.3\n" + "14.16\t53.4\t7.8\n"
						+ "21.16\t51.9\t9.6\n" + "(Rufelt, 1957)\n");

		AddData("Zea mays roots",
				"0	4	2\n" + "0.3	2	3\n" + "0.6	11	4\n" + "0.9	23	3\n" + "1.4	50	4\n" + "1.9	69	4\n"
						+ "2.4	76	4\n" + "2.9	80	4\n" + "3.9	72	5\n" + "4.9	70	5\n" + "5.8	74	6\n" + "6.8	78	4\n"
						+ "(Stockus, 1993)");

		AddData("New object", "\n\n\n(enter your data (<100 points) before this text)");

		/*
		 * AddData("Helianthus anuus hypocotyls", "(Johnson & Israelson, 1968)"
		 * ); AddData("Lepidum sativum roots", "(Hensel& Sievers, 1980)");
		 * AddData("Avena sativa coleoptiles", "(Pickard, 1973)");
		 */
	};
}

This concludes the Program listings for the model simulating the apex angle of the gravitropic reaction

GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>  Everyone is permitted to copy and distribute verbatim copies  of this license document, but changing it is not allowed.

Preamble

  The GNU General Public License is a free, copyleft license for software and other kinds of works.

  The licenses for most software and other practical works are designed to take away your freedom to share and change the works.  By contrast, the GNU General Public License is intended to guarantee your freedom toshare and change all versions of a program--to make sure it remains free software for all its users.  We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors.  You can apply it to your programs, too.

  When we speak of free software, we are referring to freedom, not price.  Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights.  Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received.  You must make sure that they, too, receive or can get the source code.  And you must show them these terms so they know their rights.

  Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software.  For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.

  Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so.  This is fundamentally incompatible with the aim of protecting users' freedom to change the software.  The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable.  Therefore, we have designed this version of the GPL to prohibit the practice for those products.  If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary.  To prevent this, the GPL assures that patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and modification follow.

TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this License.  Each licensee is addressed as "you".  "Licensees" and "recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy.  The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based on the Program.

  To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy.  Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other parties to make or receive copies.  Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License.  If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work for making modifications to it.  "Object code" means any non-source form of a work.

  A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form.  A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities.  However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work.  For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

  The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

  The Corresponding Source for a work in source code form is that same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met.  This License explicitly affirms your unlimited permission to run the unmodified Program.  The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work.  This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force.  You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright.  Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under the conditions stated below.  Sublicensing is not allowed; section 10 makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

  When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7.  This requirement modifies the requirement in section 4 to "keep intact all notices".

    c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy.  This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged.  This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

  A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit.  Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

    a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.

    d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge.  You need not require recipients to copy the Corresponding Source along with the object code.  If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source.  Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling.  In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage.  For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product.  A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source.  The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

  If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information.  But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

  The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed.  Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law.  If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it.  (Additional permissions may be written to require their own removal in certain cases when you modify the work.)  You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or authors of the material; or

    e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

  All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10.  If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term.  If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly provided under this License.  Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

  However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

  Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License.  If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or run a copy of the Program.  Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance.  However, nothing other than this License grants you permission to propagate or modify any covered work.  These actions infringe copyright if you do not accept this License.  Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License.  You are not responsible for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations.  If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License.  For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based.  The work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version.  For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement).  To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

  If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients.  "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

  A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License.  You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License.  If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all.  For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work.  The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time.  Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

  Each version is given a distinguishing version number.  If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation.  If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

  Later license versions may give you additional or different permissions.  However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

END OF TERMS AND CONDITIONS

How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>  Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.
--------------------------------------------------------------

  If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License.  Of course, your program's commands might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program into proprietary programs.  If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library.  If this is what you want to do, use the GNU Lesser General Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--------------------------------------------------------------